作者 钟来

模块整理

正在显示 26 个修改的文件 包含 891 行增加140 行删除
package com.zhonglai.luhui.security.dto;
import com.google.gson.JsonObject;
import com.ruoyi.common.utils.DESUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.GsonConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* 登录令牌
*/
@Data
public class LoginToken implements Serializable {
private static final long serialVersionUID = -8696564127500370479L;
private Integer userId; //当前用户id
private String userLoginName; //当前用户登录名
private String userNickName; //当前用户昵称
private Integer createTime; //生成时间
private String key = "LiuYuLeXX"; //密钥
private String userType; //用户类型(0普通用户,1管理员)
public LoginToken(Integer userId, String userLoginName, String userNickName, String userType) {
this.userId = userId;
this.userLoginName = userLoginName;
this.userNickName = userNickName;
this.userType = userType;
this.createTime = DateUtils.getNowTimeMilly();
}
/**
* 解密token
* @param deLoginToken
*/
public LoginToken(String deLoginToken)
{
String loginTokenString = DESUtil.decode(deLoginToken,key);
JsonObject jsonObject = GsonConstructor.get().fromJson(loginTokenString, JsonObject.class);
if(jsonObject.has("userId"))
{
userId = jsonObject.get("userId").getAsInt();
}
if(jsonObject.has("userLoginName"))
{
userLoginName = jsonObject.get("userLoginName").getAsString();
}
if(jsonObject.has("userNickName"))
{
userNickName = jsonObject.get("userNickName").getAsString();
}
if(jsonObject.has("createTime"))
{
createTime = jsonObject.get("createTime").getAsInt();
}
if(jsonObject.has("userType"))
{
userType = jsonObject.get("userType").getAsString();
}
}
/**
* 生成加密loginToken
* @return
*/
public String get()
{
return DESUtil.encode(GsonConstructor.get().toJson(this),key);
}
}
... ...
... ... @@ -19,27 +19,27 @@ import java.io.IOException;
/**
* token过滤器 验证token有效性
*
*
* @author ruoyi
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
public abstract class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
BaseLoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
BaseLoginUser loginUser = getBaseLoginUser(request);
if(verifyToken(loginUser))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(loginUser);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
chain.doFilter(request, response);
}
public abstract BaseLoginUser getBaseLoginUser(HttpServletRequest request);
public abstract boolean verifyToken(BaseLoginUser loginUser);
public abstract UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken( BaseLoginUser loginUser);
}
... ...
package com.zhonglai.luhui.security.service;
import com.ruoyi.common.tool.SysLogininforType;
import com.zhonglai.luhui.security.filter.JwtAuthenticationTokenFilter;
import com.zhonglai.luhui.security.handle.AuthenticationEntryPointImpl;
import com.zhonglai.luhui.security.handle.LogoutSuccessHandlerImpl;
... ... @@ -10,16 +9,12 @@ import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.stereotype.Service;
import org.springframework.web.filter.CorsFilter;
import javax.servlet.*;
import java.io.IOException;
@Service
public class SecurityConfigService {
... ... @@ -41,7 +36,6 @@ public class SecurityConfigService {
@Autowired
private CorsFilter corsFilter;
/**
* 退出处理类
*/
... ... @@ -60,7 +54,7 @@ public class SecurityConfigService {
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, authenticationTokenFilter.getClass());
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
}
... ...
... ... @@ -12,6 +12,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
... ... @@ -53,9 +54,9 @@ public class ChatController {
@CrossOrigin
@PostMapping("/chat")
@ResponseBody
public ChatResponse sseChat(@RequestBody ChatRequest chatRequest, @RequestHeader Map<String, String> headers, HttpServletResponse response) {
public ChatResponse sseChat(@RequestBody ChatRequest chatRequest, @RequestHeader Map<String, String> headers, HttpServletRequest request) {
String uid = getUid(headers);
return sseService.sseChat(true,0,uid, chatRequest, ChatCompletion.Model.GPT_3_5_TURBO_0301,null);
return sseService.sseChat(true,0,uid, chatRequest, ChatCompletion.Model.GPT_3_5_TURBO_0301,null,request);
}
/**
... ...
package com.zhonglai.luhui.chatgpt.entity;
import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse;
import lombok.Data;
@Data
public class MyChatCompletionResponse extends ChatCompletionResponse {
private String warning;
}
... ...
... ... @@ -3,6 +3,7 @@ package com.zhonglai.luhui.chatgpt.listener;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse;
import com.unfbx.chatgpt.entity.chat.Message;
import com.zhonglai.luhui.chatgpt.entity.MyChatCompletionResponse;
import com.zhonglai.luhui.chatgpt.event.MyEvent;
import com.zhonglai.luhui.chatgpt.service.CompleteCallback;
import lombok.SneakyThrows;
... ... @@ -14,6 +15,7 @@ import okhttp3.sse.EventSourceListener;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.util.Objects;
... ... @@ -34,12 +36,15 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
private CompleteCallback completeCallback;
private HttpServletRequest request;
private boolean isHaveData;
private int recordId;
public OpenAISSEEventSourceListener(SseEmitter sseEmitter, CompleteCallback completeCallback,int recordId) {
public OpenAISSEEventSourceListener(SseEmitter sseEmitter, CompleteCallback completeCallback,int recordId,HttpServletRequest request) {
this.sseEmitter = sseEmitter;
this.completeCallback = completeCallback;
this.recordId = recordId;
this.request = request;
}
/**
... ... @@ -77,35 +82,40 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
return;
}
ObjectMapper mapper = new ObjectMapper();
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json
MyChatCompletionResponse completionResponse = mapper.readValue(data, MyChatCompletionResponse.class); // 读取Json
String content="无法获取返回内容!";
try {
Message delta = completionResponse.getChoices().get(0).getDelta();
if(null != delta.getContent())
if(null !=completionResponse.getWarning() && !completionResponse.getWarning().equals(""))
{
String content = delta.getContent();
if(isHaveData)
Message delta = completionResponse.getChoices().get(0).getDelta();
if(null != delta.getContent())
{
// if(content.startsWith("\n") || content.endsWith("\n") )
// {
// content = content.replace("\n","~n~");
// }
sseEmitter.send(URLEncoder.encode(content,"utf-8").replaceAll("\\+", "%20"), MediaType.TEXT_EVENT_STREAM);
}else{
sseEmitter.send(new MyEvent().data(content, MediaType.TEXT_EVENT_STREAM));
content = delta.getContent();
contents.append(content);
}
contents.append(content);
}
// sseEmitter.send(SseEmitter.event()
// .id(completionResponse.getId())
// .data(delta.getContent())
// .reconnectTime(3000)
// );
}else{
content = completionResponse.getWarning();
}
if(isHaveData)
{
sseEmitter.send(URLEncoder.encode(content,"utf-8").replaceAll("\\+", "%20"), MediaType.TEXT_EVENT_STREAM);
}else{
sseEmitter.send(new MyEvent().data(content, MediaType.TEXT_EVENT_STREAM));
}
} catch (Exception e) {
log.error("sse信息推送失败!");
eventSource.cancel();
e.printStackTrace();
}
}
... ... @@ -115,7 +125,7 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
log.info("OpenAI关闭sse连接...");
if(null != completeCallback)
{
completeCallback.sseChatEnd(recordId,tokens,contents.toString());
completeCallback.sseChatEnd(recordId,tokens,contents.toString(),request);
}
}
... ...
... ... @@ -4,7 +4,9 @@ import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.zhonglai.luhui.chatgpt.controller.request.ChatRequest;
import com.zhonglai.luhui.chatgpt.listener.OpenAISSEEventSourceListener;
import javax.servlet.http.HttpServletRequest;
public interface CompleteCallback {
void sseChatEnd(int recordId,long tokens,String contents);
void sseChatEnd(int recordId, long tokens, String contents, HttpServletRequest httpServletRequest);
int recordSseChat(Integer user_id, ChatRequest chatRequest, ChatCompletion chatCompletion);
}
... ...
... ... @@ -5,6 +5,8 @@ import com.zhonglai.luhui.chatgpt.controller.request.ChatRequest;
import com.zhonglai.luhui.chatgpt.controller.response.ChatResponse;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
/**
* 描述:
*
... ... @@ -30,5 +32,5 @@ public interface SseService {
* @param uid
* @param chatRequest
*/
ChatResponse sseChat(Boolean isHaveData,Integer user_id,String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback);
ChatResponse sseChat(Boolean isHaveData, Integer user_id, String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback, HttpServletRequest request);
}
... ...
... ... @@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
... ... @@ -83,7 +84,7 @@ public class SseServiceImpl implements SseService {
}
@Override
public ChatResponse sseChat(Boolean isHaveData,Integer user_id,String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback) {
public ChatResponse sseChat(Boolean isHaveData, Integer user_id, String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback, HttpServletRequest request) {
if (ArrayUtil.isEmpty(chatRequest.getMsg())) {
log.info("参数异常,msg为null", uid);
throw new BaseException("参数异常,msg不能为空~");
... ... @@ -130,7 +131,7 @@ public class SseServiceImpl implements SseService {
recordId = completeCallback.recordSseChat(user_id,chatRequest,completion);
}
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(sseEmitter,completeCallback,recordId);
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(sseEmitter,completeCallback,recordId,request);
openAIEventSourceListener.setHaveData(isHaveData);
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
// LocalCache.CACHE.put("msg" + uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
... ...
package com.zhonglai.luhui.login.service;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.tool.SysLogininforType;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.zhonglai.luhui.security.dto.LoginToken;
import com.zhonglai.luhui.security.dto.OpenAiLoginUser;
import com.zhonglai.luhui.security.dto.OpenAiUserInfo;
import com.zhonglai.luhui.sys.manager.AsyncManager;
import com.zhonglai.luhui.sys.manager.factory.AsyncFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
@Service
public class LocalLoginService {
@Autowired
private LoginService loginService;
public String openaiLoginByPass(String user,String pass) {
// 用户验证
Authentication authentication = loginService.userPasswordVerification(user,pass, SpringUtils.getBean("openAiConfigurerAdapter"));
AsyncManager.me().execute(AsyncFactory.recordLogininfor(user, SysLogininforType.openAi, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
OpenAiLoginUser loginUser = (OpenAiLoginUser) authentication.getPrincipal();
loginUser.setSysLogininforType(SysLogininforType.openAi);
return createToken(loginUser);
}
public String createToken(OpenAiLoginUser loginUser)
{
OpenAiUserInfo openAiUserInfo = loginUser.getOpenAiUserInfo();
LoginToken loginToken = new LoginToken(openAiUserInfo.getId(),openAiUserInfo.getPhone(),openAiUserInfo.getNickname(),loginUser.getSysLogininforType().name());
return loginToken.get();
}
}
... ...
... ... @@ -151,7 +151,7 @@ public class LoginService {
}
}
private Authentication userPasswordVerification(String username, String password, DefaultSecurityConfig defaultSecurityConfig)
public Authentication userPasswordVerification(String username, String password, DefaultSecurityConfig defaultSecurityConfig)
{
Authentication authentication = null;
try
... ...
package com.zhonglai.luhui.openai;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.ResourcesConfig;
import com.zhonglai.luhui.security.filter.JwtAuthenticationTokenFilter;
import okhttp3.OkHttpClient;
import org.apache.tomcat.util.http.LegacyCookieProcessor;
import org.springframework.boot.SpringApplication;
... ... @@ -11,6 +13,7 @@ import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactor
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import java.io.UnsupportedEncodingException;
import java.net.URL;
... ...
package com.zhonglai.luhui.openai.config;
import com.ruoyi.common.tool.SysLogininforType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.zhonglai.luhui.dao.service.PublicService;
import com.zhonglai.luhui.security.dto.BaseLoginUser;
import com.zhonglai.luhui.security.dto.LoginToken;
import com.zhonglai.luhui.security.dto.OpenAiLoginUser;
import com.zhonglai.luhui.security.dto.OpenAiUserInfo;
import com.zhonglai.luhui.security.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class JwtAuthenticationTokenFilterImpl extends JwtAuthenticationTokenFilter {
@Value("${token.header}")
private String header;
@Value("${token.expireTime}")
private Long expireTime;
@Autowired
private PublicService publicService;
@Override
public BaseLoginUser getBaseLoginUser(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token))
{
try {
LoginToken loginToken = new LoginToken(token);
BaseLoginUser baseLoginUser = to(loginToken);
baseLoginUser.setExpireTime(loginToken.getCreateTime()+expireTime);
return baseLoginUser;
}catch (Exception e)
{
logger.error("token验证失败",e);
}
return null;
}
return null;
}
@Override
public boolean verifyToken(BaseLoginUser baseLoginUser) {
if(null != baseLoginUser)
{
Integer currentTime = DateUtils.getNowTimeMilly();
Long expireTime = baseLoginUser.getExpireTime();
if (expireTime - currentTime > 0)
{
return true;
}
}
return false;
}
@Override
public UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(BaseLoginUser loginUser) {
return new UsernamePasswordAuthenticationToken(loginUser, null, null);
}
public BaseLoginUser to(LoginToken loginToken) {
OpenAiUserInfo openAiUserInfo = publicService.getObjectForTableName(OpenAiUserInfo.class,"id", loginToken.getUserId()+"","`lk_openai`.`user_info`");
OpenAiLoginUser openAiLoginUser = new OpenAiLoginUser();
openAiLoginUser.setUserId(Long.parseLong(openAiUserInfo.getId()+""));
openAiLoginUser.setOpenAiUserInfo(openAiUserInfo);
openAiLoginUser.setSysLogininforType(SysLogininforType.openAi);
return openAiLoginUser;
}
}
... ...
... ... @@ -31,6 +31,7 @@ public class OpenAiConfigurerAdapter extends DefaultSecurityConfig {
@Override
public void configHttpSecurity(HttpSecurity httpSecurity) throws Exception {
securityConfigService.configHttpSecurity(httpSecurity);
}
@Override
... ...
... ... @@ -12,6 +12,7 @@ import com.ruoyi.common.utils.GsonConstructor;
import com.ruoyi.common.utils.StringUtils;
import com.zhonglai.luhui.action.BaseController;
import com.zhonglai.luhui.dao.service.PublicService;
import com.zhonglai.luhui.login.service.LocalLoginService;
import com.zhonglai.luhui.login.service.LoginService;
import com.zhonglai.luhui.security.dto.OpenAiUserInfo;
import io.swagger.annotations.Api;
... ... @@ -34,7 +35,7 @@ public class OpenAiUserLoginController extends BaseController {
public static String ENCODE_KEY = "com/zhonglai";
@Autowired
private LoginService loginService;
private LocalLoginService loginService;
@Autowired
private PublicService publicService;
... ...
... ... @@ -2,17 +2,16 @@ package com.zhonglai.luhui.openai.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.zhonglai.luhui.action.BaseController;
import com.zhonglai.luhui.dao.service.PublicService;
import com.zhonglai.luhui.openai.dto.UserRoom;
import com.zhonglai.luhui.security.dto.OpenAiUserInfo;
import com.zhonglai.luhui.security.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
... ... @@ -29,15 +28,29 @@ public class UserRoomController extends BaseController {
public TableDataInfo getUserRoomList()
{
startPage();
List<Map<String,Object>> list=publicService.getObjectListBySQL("SELECT * FROM `user_room` WHERE user_id="+SecurityUtils.getUserId().toString()+" AND is_delete=1");
List<Map<String,Object>> list=publicService.getObjectListBySQL("SELECT * FROM `lk_openai`.`user_room` WHERE user_id="+SecurityUtils.getUserId().toString()+" AND is_delete=1");
return getDataTable(list);
}
// @ApiOperation("添加话提")
// @PostMapping("/addUserRoom")
// public AjaxResult addUserRoom()
// {
//
// }
@ApiOperation("添加话提")
@PostMapping("/addUserRoom")
public AjaxResult addUserRoom(String roomTitle)
{
UserRoom userRoom = new UserRoom();
userRoom.setUser_id(SecurityUtils.getUserId().intValue());
userRoom.setTitle(roomTitle);
userRoom.setIs_delete(1);
userRoom.setCreate_time(DateUtils.getNowTimeMilly());
int i = publicService.insertToTable(userRoom,"`lk_openai`.`user_room`");
return toAjax(i).put("roomId",userRoom.getId());
}
@ApiOperation("删除话提")
@DeleteMapping("/del/{id}")
public AjaxResult del(@PathVariable Integer id)
{
return toAjax(publicService.updateBySql("UPDATE `lk_openai`.`user_room` SET is_delete=0 WHERE id="+id+" and user_id="+SecurityUtils.getUserId()));
}
}
... ...
package com.zhonglai.luhui.openai.dto;
import lombok.Data;
@Data
public class UserRoom {
private Integer id; // int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
private String title; // varchar(150) DEFAULT NULL COMMENT '标题',
private Integer user_id; // int(11) DEFAULT NULL COMMENT '用户',
private Integer create_time; // int(11) DEFAULT NULL COMMENT '创建时间',
private Integer is_delete; // int(11) DEFAULT NULL COMMENT '删除',
}
... ...
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 获取ip地址开关 addressEnabled: false profile: /www/wwwroot/lh-openai # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8082 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # web: # resources: # static-locations: classpath:/static/, classpath:/templates/ # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-openai # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/openAiUserLogin/*,/chatGPTStream/upUserFlowPacketRemain,/createSse,/chat,/closeSse chatgpt: token: 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 timeout: 5000 apiHost: https://api.openai.com/ proxy: isProxy: true host: 127.0.0.1 port: 7890
\ No newline at end of file
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 获取ip地址开关 addressEnabled: false profile: /www/wwwroot/lh-openai # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8082 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # web: # resources: # static-locations: classpath:/static/, classpath:/templates/ # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 31536000 rediskey: lh-openai # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/openAiUserLogin/*,/chatGPTStream/upUserFlowPacketRemain,/createSse,/chat,/closeSse chatgpt: token: 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 timeout: 5000 apiHost: https://api.openai.com/ proxy: isProxy: true host: 127.0.0.1 port: 7890
\ No newline at end of file
... ...
package com.zhonglai.luhui.smart.feeder;
import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
import com.zhonglai.luhui.smart.feeder.service.OpenCVService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
... ...
package com.zhonglai.luhui.smart.feeder.ai;
import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.util.ArrayList;
import java.util.List;
public class AdaptiveThresholdTuning {
public static void main(String[] args) {
OpenCVConfig.loadOpenCv(args);
// 加载标准水面区域图片
List<Mat> standardImages = loadStandardImages();
// 初始化最佳参数和最佳分割结果
int bestBlockSize = -1;
double bestC = -1;
double bestAccuracy = 0;
// 遍历不同的参数组合进行测试
for (int blockSize = 3; blockSize <= 21; blockSize += 2) {
for (double c = -20; c <= 20; c += 2) {
double totalAccuracy = 0;
// 在标准图片上进行测试并计算准确度
for (Mat standardImage : standardImages) {
double accuracy = testAdaptiveThreshold(standardImage, blockSize, c);
totalAccuracy += accuracy;
}
// 计算平均准确度
double averageAccuracy = totalAccuracy / standardImages.size();
// 更新最佳参数和最佳准确度
if (averageAccuracy > bestAccuracy) {
bestAccuracy = averageAccuracy;
bestBlockSize = blockSize;
bestC = c;
}
}
}
// 输出最佳参数
System.out.println("Best block size: " + bestBlockSize);
System.out.println("Best C: " + bestC);
}
// 加载标准水面区域图片
private static List<Mat> loadStandardImages() {
List<Mat> standardImages = new ArrayList<>();
standardImages.add(Imgcodecs.imread("C:\\Users\\123\\Pictures\\2\\0.jpg", Imgcodecs.IMREAD_GRAYSCALE));
standardImages.add(Imgcodecs.imread("C:\\Users\\123\\Pictures\\2\\1.jpg", Imgcodecs.IMREAD_GRAYSCALE));
standardImages.add(Imgcodecs.imread("C:\\Users\\123\\Pictures\\2\\2.jpg", Imgcodecs.IMREAD_GRAYSCALE));
// ... 加载更多的标准图片
return standardImages;
}
// 测试适应性阈值分割并返回准确度
private static double testAdaptiveThreshold(Mat standardImage, int blockSize, double c) {
// 创建用于分割结果的图像
Mat segmentedImg = new Mat();
// 应用适应性阈值分割
Imgproc.adaptiveThreshold(standardImage, segmentedImg, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, blockSize, c);
// 计算准确度
double accuracy = calculateAccuracy(segmentedImg, standardImage);
return accuracy;
}
// 计算分割结果与标准图片的准确度
private static double calculateAccuracy(Mat segmentedImg, Mat standardImage) {
// 调整分割结果图片的大小为标准图片的大小
Mat resizedSegmentedImg = new Mat();
Imgproc.resize(segmentedImg, resizedSegmentedImg, standardImage.size());
int totalPixels = standardImage.cols() * standardImage.rows(); // 图像总像素数
int matchedPixels = 0; // 匹配的像素数
for (int i = 0; i < standardImage.rows(); i++) {
for (int j = 0; j < standardImage.cols(); j++) {
double segmentedPixel = resizedSegmentedImg.get(i, j)[0];
double standardPixel = standardImage.get(i, j)[0];
// 获取透明度通道
double segmentedOpacity = resizedSegmentedImg.get(i, j)[0]; // 假设透明度在分割结果图片的第二通道
// 判断是否匹配像素
boolean isMatched = false;
if (Math.abs(segmentedPixel - standardPixel) <= 10) { // 考虑反光的偏差范围
// 判断透明度准确度
double standardOpacity = standardImage.get(i, j)[0]; // 假设透明度在标准图片的第二通道
if (Math.abs(segmentedOpacity - standardOpacity) <= 10) {
isMatched = true;
}
}
if (isMatched) {
matchedPixels++;
}
}
}
double accuracy = (double) matchedPixels / totalPixels;
return accuracy;
}
}
... ...
package com.zhonglai.luhui.smart.feeder.ai;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
/**
* 特征提取:使用适当的图像处理技术提取透明度、亮度和反光特性。
* 例如,可以使用像素值来计算透明度,使用图像直方图、颜色空间转换或滤波器来计算亮度,使用反光度量算法来计算反光特性。
*/
public class ImgFeatureExtractionUtil {
/**
* 透明度特征提取
* @param image
* @return
*/
public static double extractTransparencyFeature(Mat image) {
// 提取透明度特征
Mat alphaChannel = new Mat();
Core.extractChannel(image, alphaChannel, 3);
Scalar mean = Core.mean(alphaChannel);
return mean.val[0];
}
/**
* 亮度特征提取
* @param image
* @return
*/
public static double extractBrightnessFeature(Mat image) {
// 转换为灰度图像
Mat grayImage = new Mat();
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
Scalar mean = Core.mean(grayImage);
return mean.val[0];
}
/**
* 反光特征提取
* @param image
* @return
*/
public static double extractReflectionFeature(Mat image) {
// 转换为灰度图像
Mat grayImage = new Mat();
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
// 计算反光特征
Mat binaryImage = new Mat();
double thresholdValue = 100; // 调整阈值以根据图像适应性地检测反光
double maxValue = 255;
Imgproc.threshold(grayImage, binaryImage, thresholdValue, maxValue, Imgproc.THRESH_BINARY);
return Core.countNonZero(binaryImage);
}
}
... ...
package com.zhonglai.luhui.smart.feeder.draw;
import com.zhonglai.luhui.smart.feeder.util.OpenCVUtils;
import org.opencv.core.Mat;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
public class FishRegionPanel
{
private JFrame frame;
private JLabel lblImage;
private JLabel srcImage;
private GraphPanel pnlGraph;
private void init() {
frame = new JFrame("Fish Detection");
frame.setSize(1000,1000);
frame.setLayout(null);
lblImage = new JLabel();
srcImage = new JLabel();
pnlGraph = new GraphPanel(new ArrayList<>());
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(lblImage, BorderLayout.NORTH);
frame.add(srcImage, BorderLayout.SOUTH);
frame.add(pnlGraph, BorderLayout.CENTER);
// frame.pack();
frame.setVisible(true);
}
public void end()
{
frame.setVisible(true);
}
public FishRegionPanel() {
init();
}
public void addFishCount(int size) {
pnlGraph.getFishCountList().add(size);
pnlGraph.repaint();
}
public void displayImage(Mat image) {
lblImage.setIcon(new ImageIcon(convertMatToImage(image)));
frame.repaint();
}
public void dispSrcImage(Mat image) {
srcImage.setIcon(new ImageIcon(convertMatToImage(image)));
frame.repaint();
}
private Image convertMatToImage(Mat mat) {
BufferedImage bufferedImage = OpenCVUtils.matToBufferedImage(mat);
return bufferedImage.getScaledInstance(300, 300, Image.SCALE_SMOOTH);
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.smart.feeder.draw;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class GraphPanel extends JPanel {
private List<Integer> fishCountList;
Dimension preferredSize = new Dimension(300, 300);
public GraphPanel(List<Integer> fishCountList) {
this.fishCountList = fishCountList;
}
public List<Integer> getFishCountList() {
return this.fishCountList;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
// g2d.setStroke(new BasicStroke(2));
if (!fishCountList.isEmpty()) {
int panelWidth = 300;
int panelHeight = 300;
int countSize = fishCountList.size();
int[] counts = new int[countSize];
double max = 0;
for (int i = 0; i < countSize; i++) {
counts[i] = fishCountList.get(i);
if(counts[i]>max)
{
max = counts[i];
}
}
int x = 0;
int y = new Double((counts[0]/max)*panelHeight).intValue();
for (int i = 1; i < countSize; i++) {
int nextX = (int) (panelWidth * i / countSize);
int nextY = new Double((counts[i]/max)*panelHeight).intValue();
g2d.drawLine(x, y, nextX, nextY);
x = nextX;
y = nextY;
}
}
}
}
... ...
package com.zhonglai.luhui.smart.feeder.service;
import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
import com.zhonglai.luhui.smart.feeder.draw.FishRegionPanel;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import java.util.ArrayList;
import java.util.List;
public class FileLook {
public static void main(String[] args) {
OpenCVConfig.loadOpenCv(args);
VideoCapture videoCapture = new VideoCapture();
boolean isopen = videoCapture.open("C:/Users/123/Pictures/1.mp4");
if(isopen)
{
identifyFishRegionOnWaterSurface(videoCapture);
}else {
System.out.println("无法读取视频帧");
}
}
/**
* 先识别水面,然后再在水面区域查找鱼群区域
*/
private static void identifyFishRegionOnWaterSurface(VideoCapture videoCapture) {
// 读取第一帧并获取视频大小
Mat previousFrame = new Mat();
if (!videoCapture.read(previousFrame)) {
System.out.println("无法读取视频帧");
return;
}
FishRegionPanel fishRegionPanel = new FishRegionPanel();
// 转换为灰度图像
Mat previousGray = new Mat();
Imgproc.cvtColor(previousFrame, previousGray, Imgproc.COLOR_BGR2GRAY);
// 识别水面区域
Rect waterSurfaceRegion = identifyWaterSurface(previousGray);
// 逐帧处理视频
Mat frame = new Mat();
while (videoCapture.read(frame)) {
// 在水面区域查找鱼群区域
Mat fishRegion = findFishRegion(frame, waterSurfaceRegion);
// 绘制鱼群变化曲线
// fishRegionPanel.displayImage(fishRegion);
fishRegionPanel.dispSrcImage(frame);
}
}
private static Rect identifyWaterSurface(Mat grayImage) {
// 进行自适应阈值分割,根据水面的亮度特征,将水面与其他区域分离
Mat binaryImage = new Mat();
Imgproc.adaptiveThreshold(grayImage, binaryImage, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 11, 5);
// 进行形态学操作,去除噪点
int kernelSize = 5; // 调整内核大小
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
Imgproc.morphologyEx(binaryImage, binaryImage, Imgproc.MORPH_OPEN, kernel);
// 查找水面区域的轮廓
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 获取最大的轮廓区域作为水面区域
double maxContourArea = -1;
Rect waterSurfaceRegion = null;
for (MatOfPoint contour : contours) {
double contourArea = Imgproc.contourArea(contour);
if (contourArea > maxContourArea) {
waterSurfaceRegion = Imgproc.boundingRect(contour);
maxContourArea = contourArea;
}
}
return waterSurfaceRegion;
}
private static Mat findFishRegion(Mat frame, Rect waterSurfaceRegion) {
// 在水面区域查找鱼群区域
Mat fishRegion = frame.submat(waterSurfaceRegion);
// 绘制绿色线框
Scalar green = new Scalar(0, 255, 0); // 绿色
Imgproc.rectangle(frame, waterSurfaceRegion, green, 2); // 绘制矩形框
// 转换为灰度图像
Mat grayImage = new Mat();
Imgproc.cvtColor(fishRegion, grayImage, Imgproc.COLOR_BGR2GRAY);
// 进行亮度过滤
double brightnessThreshold = 100; // 亮度阈值
Mat binaryImage = new Mat();
Imgproc.threshold(grayImage, binaryImage, brightnessThreshold, 255, Imgproc.THRESH_BINARY);
// 进行透明度过滤(如果需要)
// 如果图像中有透明度通道(例如RGBA图像),可以提取透明度通道并进行阈值过滤
// 如果图像中没有透明度通道,则可以跳过这部分代码
Mat alphaChannel = new Mat();
if (fishRegion.channels() == 4) {
Core.extractChannel(fishRegion, alphaChannel, 3); // 提取透明度通道
double alphaThreshold = 100; // 透明度阈值
Mat filteredImage = new Mat();
Imgproc.threshold(alphaChannel, filteredImage, alphaThreshold, 255, Imgproc.THRESH_BINARY);
Core.bitwise_and(binaryImage, filteredImage, binaryImage); // 组合亮度过滤和透明度过滤的结果
}
// 对二值图像进行形态学操作,去除噪点
int kernelSize = 3; // 调整内核大小
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new org.opencv.core.Size(kernelSize, kernelSize));
Imgproc.morphologyEx(binaryImage, binaryImage, Imgproc.MORPH_OPEN, kernel);
// 将二值图像转换回BGR图像
Mat filteredRegion = new Mat();
Imgproc.cvtColor(binaryImage, filteredRegion, Imgproc.COLOR_GRAY2BGR);
return filteredRegion;
}
}
... ...
package com.zhonglai.luhui.smart.feeder.service;
import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
import com.zhonglai.luhui.smart.feeder.util.OpenCVUtils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import com.zhonglai.luhui.smart.feeder.draw.FishRegionPanel;
import org.opencv.core.*;
import org.opencv.videoio.VideoCapture;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Scalar;
import org.opencv.core.CvType;
import org.opencv.core.Rect;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class OpenCVService {
/**
* 反光阈值(reflectionThreshold)被设置为100。这意味着所有灰度值低于100的像素都会被设置为0(黑色),灰度值大于或等于100的像素都会被设置为255(白色)。如果你的图像中的对象或区域的灰度值接近或低于这个阈值,它们可能会被排除在二值图像之外。尝试调整这个阈值可能有助于改善结果
*/
public static int reflectionThreshold = 100; // 反光阈值
public static int kernelSize = 3; // 去噪调整内核大小,用来消除小的物体或噪声
public static void main(String[] args) {
OpenCVConfig.loadOpenCv(args);
readVideoCaptureForVideo("C:/Users/123/Pictures/图片识别/6月30日.mp4");
readVideoCaptureForVideo("C:/Users/123/Pictures/1.mp4");
}
public static void readVideoCaptureForVideo(String videoPath)
{
// 创建VideoCapture对象
... ... @@ -32,86 +34,168 @@ public class OpenCVService {
System.out.println("无法打开视频文件");
return;
}
// 背景帧
Mat backgroundFrame = new Mat();
brightnessIdentifyFishRegion(videoCapture);
// 释放资源
videoCapture.release();
}
/**
* 亮度查找水面,透明度过滤鱼群
*/
private static void brightnessIdentifyFishRegion(VideoCapture videoCapture)
{
// 读取第一帧并获取视频大小
Mat previousFrame = new Mat();
if (!videoCapture.read(previousFrame)) {
System.out.println("无法读取视频帧");
return;
}
// 获取水域轮廓
MatOfPoint largestContour = getDefaultMatOfPoint(previousFrame);
// 初始阈值范围
double minAreaThreshold = Double.MAX_VALUE;
double maxAreaThreshold = 0;
//画板
FishRegionPanel fishRegionPanel = new FishRegionPanel();
// 逐帧处理视频
Mat frame = new Mat();
while (videoCapture.read(frame)) {
// 背景差分
Mat diffFrame = new Mat();
Core.absdiff(frame, backgroundFrame, diffFrame);
// 灰度转换
Mat grayFrame = new Mat();
Imgproc.cvtColor(diffFrame, grayFrame, Imgproc.COLOR_BGR2GRAY);
// 阈值处理
Mat thresholdFrame = new Mat();
Imgproc.threshold(grayFrame, thresholdFrame, 30, 255, Imgproc.THRESH_BINARY);
// 边缘检测
Mat edges = new Mat();
Imgproc.Canny(thresholdFrame, edges, 100, 200);
// 轮廓检测
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 更新阈值范围
for (MatOfPoint contour : contours) {
double area = Imgproc.contourArea(contour);
if (area > maxAreaThreshold) {
maxAreaThreshold = area;
}
if (area < minAreaThreshold) {
minAreaThreshold = area;
}
}
//抠图
Mat shuiyu = matting(frame,largestContour);
// 根据阈值范围选择适当的阈值
double thresholdValue = (maxAreaThreshold + minAreaThreshold) / 2.0;
// 绘制轮廓
Mat contourImage = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(0, 0, 0));
Imgproc.drawContours(contourImage, contours, -1, new Scalar(0, 255, 0), 2);
// 提取鱼群区域
for (MatOfPoint contour : contours) {
double area = Imgproc.contourArea(contour);
if (area > thresholdValue) {
// 对于满足面积阈值的轮廓,可以进一步处理或分析
// 例如,计算鱼群数量、中心位置等信息
// ...
// 在原图上绘制鱼群区域
Rect boundingRect = Imgproc.boundingRect(contour);
Imgproc.rectangle(frame, boundingRect.tl(), boundingRect.br(), new Scalar(0, 255, 0), 2);
}
}
// 重置阈值范围
minAreaThreshold = Double.MAX_VALUE;
maxAreaThreshold = 0;
// 2. 转换为灰度图像
Mat gray = new Mat();
Imgproc.cvtColor(shuiyu, gray, Imgproc.COLOR_BGR2GRAY);
// 3. 进行阈值分割以得到二值图像
Mat binaryImage = new Mat();
Imgproc.threshold(gray, binaryImage, 100, 255, Imgproc.THRESH_BINARY);
List<MatOfPoint> contours = new ArrayList<>(); // 用于存储找到的轮廓
Mat hierarchy = new Mat(); // 轮廓的层次结构
// 在水域二值图像中找所有轮廓
Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
//计算大小
double area = getArea(contours);
//标注识别对象
Imgproc.drawContours(frame, contours, -1, new Scalar(0, 0, 255), 2);
Imgproc.drawContours(frame, Arrays.asList(new MatOfPoint[]{largestContour}), 0, new Scalar(0, 255, 0), 2);
// 显示图像
// 在图像上显示结果
displayImage(frame);
fishRegionPanel.displayImage(binaryImage);
fishRegionPanel.dispSrcImage(frame);
// 绘制鱼群变化曲线
fishRegionPanel.addFishCount(new Double(area).intValue());
}
}
// 显示图像
private static void displayImage(Mat image) {
// 将Mat图像转换为BufferedImage
BufferedImage bufferedImage = OpenCVUtils.matToBufferedImage(image);
/**
* 获取标准水域轮廓
* @param previousFrame
* @return
*/
private static MatOfPoint getDefaultMatOfPoint(Mat previousFrame)
{
Mat firstBinaryImage = waterBybinary(previousFrame);
// 绘制白色区域的轮廓
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(firstBinaryImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 找到最大区域
double maxArea = 0;
int maxAreaIndex = -1;
for (int i = 0; i < contours.size(); i++) {
double area = Imgproc.contourArea(contours.get(i));
if (area > maxArea) {
maxArea = area;
maxAreaIndex = i;
}
}
// 获取最大区域的轮廓
MatOfPoint largestContour = contours.get(maxAreaIndex);
return largestContour;
}
private static double getArea(List<MatOfPoint> contours) {
// 找到最大区域
double maxArea = 0;
int maxAreaIndex = -1;
for (int i = 0; i < contours.size(); i++) {
double area = Imgproc.contourArea(contours.get(i));
if (area > maxArea) {
maxArea = area;
maxAreaIndex = i;
}
}
if(-1 != maxAreaIndex)
{
contours.remove(maxAreaIndex);
}
// 返回总面积
return maxArea;
}
// 在标签上显示图像
new ImageIcon(bufferedImage);
// 更新窗口
private static Mat matting(Mat frame,MatOfPoint largestContour)
{
// 创建一个与原始图像相同大小的新Mat,用于提取图像区域
Mat extractedRegion = Mat.zeros(frame.size(), frame.type());
// 将指定的轮廓绘制到新的Mat上
Imgproc.drawContours(extractedRegion, Collections.singletonList(largestContour), 0, new Scalar(255, 255, 255), -1);
// 使用按位与操作提取对应的图像区域
Mat extractedImage = new Mat();
Core.bitwise_and(frame, extractedRegion, extractedImage);
return extractedImage;
}
/**
* 根据反光查找水面
* @param frame
* @return
*/
private static Mat waterBybinary(Mat frame) {
// 将加载的图像转换为灰度图像,以便进行亮度或反光的分析
Mat grayImage = new Mat();
Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
// 检测反光
Mat binaryImage = new Mat();
double maxValue = 255;
Imgproc.threshold(grayImage, binaryImage, reflectionThreshold, maxValue, Imgproc.THRESH_BINARY);
// 进行形态学操作,去除噪点
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
Imgproc.morphologyEx(binaryImage, binaryImage, Imgproc.MORPH_OPEN, kernel);
return binaryImage;
}
private static List<MatOfPoint> fishByWater(Mat frame)
{
// 2. 转换为灰度图像
Mat gray = new Mat();
Imgproc.cvtColor(frame, gray, Imgproc.COLOR_BGR2GRAY);
// 3. 进行阈值分割以得到二值图像
Mat binary = new Mat();
Imgproc.threshold(gray, binary, 100, 255, Imgproc.THRESH_BINARY);
// 4. 查找轮廓
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
return contours;
}
}
... ...
... ... @@ -18,7 +18,7 @@ public class VideoUtil {
* @throws Exception
*/
public static void fetchPic(File file, String framefile, int second) throws Exception{
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
FFmpegFrameGrabber ff = new FFmpegFrameGrabber( file);
ff.start();
int lenght = ff.getLengthInAudioFrames();
System.out.println(ff.getFrameRate());
... ... @@ -32,16 +32,22 @@ public class VideoUtil {
int i = 0;
Frame frame = null;
while (i < lenght) {
frame = ff.grabImage();
if (i>=(int) (ff.getFrameRate()*second)&&frame.image != null) {
System.out.print(i+",");
if(frame!=null&&frame.image!=null) {
System.out.println(i);
writeToFile(framefile,frame, i);
try {
frame = ff.grabImage();
if (i>=(int) (ff.getFrameRate()*second)&&frame.image != null) {
System.out.print(i+",");
if(frame!=null&&frame.image!=null) {
System.out.println(i);
writeToFile(framefile,frame, i);
}
second++;
}
second++;
i++;
}catch (Exception e)
{
System.out.println(e);
}
i++;
}
ff.stop();
}
... ... @@ -88,7 +94,7 @@ public class VideoUtil {
public static void main(String[] args){
try {
OpenCVConfig.loadOpenCv(args);
File file = new File("C:\\Users\\123\\Pictures\\图片识别\\20210107_100743.mp4");
File file = new File("C:/Users/123/Pictures/1.mp4");
VideoUtil.fetchPic(file,"C:\\Users\\123\\Pictures\\图片识别\\1\\",100);
System.out.println(VideoUtil.getVideoTime(file));
} catch (Exception e) {
... ...