From a4a9987e852325a56e1427ece5a5e4b982f61fc1 Mon Sep 17 00:00:00 2001 From: Imesha Sudasingha Date: Thu, 15 Mar 2018 23:56:33 +0530 Subject: [PATCH] Added camera group support to dashboard --- .../java/org/eduze/fyp/api/Constants.java | 5 +- .../org/eduze/fyp/api/model/CameraConfig.java | 11 +++ .../org/eduze/fyp/api/model/CameraGroup.java | 74 +++++++++++++++++ .../helpers/PointListToStringConverter.java | 6 ++ .../org/eduze/fyp/api/util/ImageUtils.java | 2 +- .../eduze/fyp/core/db/dao/AbstractDAO.java | 19 ++--- .../fyp/core/db/dao/AbstractDAOImpl.java | 25 ++++++ .../fyp/core/db/dao/CameraConfigDAO.java | 9 +- .../fyp/core/db/dao/CameraConfigDAOImpl.java | 34 ++++---- cramp-ui/src/main/resources/etc/spring.xml | 1 + .../java/org/eduze/fyp/web/RestServer.java | 20 ++--- .../fyp/web/controllers/ConfigController.java | 26 +++++- .../eduze/fyp/web/services/ConfigService.java | 39 +++++++-- ngapp/src/app/resources/camera-config.ts | 29 ++++++- ngapp/src/app/resources/camera-group.ts | 53 ++++++++++++ ngapp/src/app/resources/global-map.ts | 12 ++- ngapp/src/app/services/config.service.ts | 78 ++++++++--------- .../point-mapping.component.html | 62 +++++++++++--- .../point-mapping.component.spec.ts | 44 ---------- .../point-mapping/point-mapping.component.ts | 69 ++++++++++++--- sense/LightWeightCamServer.py | 10 +-- sense/OpenPersonDetector.py | 18 ++-- sense/test.py | 8 +- sense/test_videos/Mapper.py | 83 +++++++++++++++++++ sense/test_videos/VideoLoader.py | 38 +++++++++ sense/test_videos/videos.conf | 10 +++ 26 files changed, 610 insertions(+), 175 deletions(-) create mode 100644 cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java rename ngapp/src/app/services/config.service.spec.ts => cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java (73%) create mode 100644 cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java create mode 100644 ngapp/src/app/resources/camera-group.ts delete mode 100644 ngapp/src/app/settings/point-mapping/point-mapping.component.spec.ts create mode 100644 sense/test_videos/Mapper.py create mode 100644 sense/test_videos/videos.conf diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/Constants.java b/cramp-api/src/main/java/org/eduze/fyp/api/Constants.java index 789d10c9..822b767c 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/Constants.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/Constants.java @@ -43,8 +43,11 @@ private Constants() { } /** Width and hight of the standardized camera views */ public static final int CAMERA_VIEW_WIDTH = 500; public static final int CAMERA_VIEW_HEIGHT = 500; + /** Width and height of map images */ + public static final int MAP_IMAGE_WIDTH = 500; + public static final int MAP_IMAGE_HEIGHT = 500; - /** Represents the zone -> world*/ + /** Represents the zone -> world */ public static final String ZONE_NAME_WORLD = "World"; public static class Properties { diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java index f45509bd..3a43eabe 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraConfig.java @@ -54,6 +54,9 @@ public class CameraConfig { @OneToOne(mappedBy = "cameraConfig", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private PointMapping pointMapping = new PointMapping(); + @ManyToOne + private CameraGroup cameraGroup; + public byte[] getView() { return view; } @@ -94,6 +97,14 @@ public void setId(int id) { this.id = id; } + public CameraGroup getCameraGroup() { + return cameraGroup; + } + + public void setCameraGroup(CameraGroup cameraGroup) { + this.cameraGroup = cameraGroup; + } + public String toString() { return String.format("{ camera : %s, ipAndPort : %s, pointMappings: %s }", cameraId, ipAndPort, pointMapping); } diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java new file mode 100644 index 00000000..eda2c782 --- /dev/null +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/CameraGroup.java @@ -0,0 +1,74 @@ +/* + * + */ +package org.eduze.fyp.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.OneToMany; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; +import java.util.Set; + +@XmlRootElement +@Entity +@JsonIgnoreProperties("cameraConfigs") +public class CameraGroup { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private int id; + + @Column(unique = true) + private String name; + + @Lob + @Column(columnDefinition = "LONGBLOB") + private byte[] map; + + @XmlTransient + @OneToMany(mappedBy = "cameraGroup") + private Set cameraConfigs; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public byte[] getMap() { + return map; + } + + public void setMap(byte[] map) { + this.map = map; + } + + public Set getCameraConfigs() { + return cameraConfigs; + } + + public void setCameraConfigs(Set cameraConfigs) { + this.cameraConfigs = cameraConfigs; + } + + public String toString() { + return String.format("%s", name); + } +} diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/model/helpers/PointListToStringConverter.java b/cramp-api/src/main/java/org/eduze/fyp/api/model/helpers/PointListToStringConverter.java index b186c0ec..6818befc 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/model/helpers/PointListToStringConverter.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/model/helpers/PointListToStringConverter.java @@ -23,8 +23,10 @@ package org.eduze.fyp.api.model.helpers; import org.eduze.fyp.api.resources.Point; +import org.springframework.util.StringUtils; import javax.persistence.AttributeConverter; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,6 +42,10 @@ public String convertToDatabaseColumn(List points) { @Override public List convertToEntityAttribute(String stringPoints) { + if (StringUtils.isEmpty(stringPoints)) { + return Collections.emptyList(); + } + return Stream.of(stringPoints.split(";")) .map(str -> { String[] parts = str.split(","); diff --git a/cramp-api/src/main/java/org/eduze/fyp/api/util/ImageUtils.java b/cramp-api/src/main/java/org/eduze/fyp/api/util/ImageUtils.java index e8a9c253..b1571dfc 100644 --- a/cramp-api/src/main/java/org/eduze/fyp/api/util/ImageUtils.java +++ b/cramp-api/src/main/java/org/eduze/fyp/api/util/ImageUtils.java @@ -54,7 +54,7 @@ public static BufferedImage byteArrayToBufferedImage(byte[] array) throws IOExce public static BufferedImage resize(BufferedImage img, int newW, int newH) { Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH); - BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB); + BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = dimg.createGraphics(); g2d.drawImage(tmp, 0, 0, null); diff --git a/ngapp/src/app/services/config.service.spec.ts b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java similarity index 73% rename from ngapp/src/app/services/config.service.spec.ts rename to cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java index 84a4a52d..54f22500 100644 --- a/ngapp/src/app/services/config.service.spec.ts +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAO.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Eduze + * Copyright 2018 Eduze * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -17,18 +17,9 @@ * IN THE SOFTWARE. */ -import {inject, TestBed} from '@angular/core/testing'; +package org.eduze.fyp.core.db.dao; -import {ConfigService} from './config.service'; +public interface AbstractDAO { -describe('ConfigService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ConfigService] - }); - }); - - it('should be created', inject([ConfigService], (service: ConfigService) => { - expect(service).toBeTruthy(); - })); -}); + void save(Object object); +} diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java new file mode 100644 index 00000000..94d72d67 --- /dev/null +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/AbstractDAOImpl.java @@ -0,0 +1,25 @@ +/* + * + */ +package org.eduze.fyp.core.db.dao; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +public class AbstractDAOImpl implements AbstractDAO { + + protected SessionFactory sessionFactory; + + @Override + public void save(Object object) { + Session session = this.sessionFactory.openSession(); + session.beginTransaction(); + session.persist(object); + session.getTransaction().commit(); + session.close(); + } + + public void setSessionFactory(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } +} diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java index 6baa0cff..bf9baa1c 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAO.java @@ -20,17 +20,20 @@ package org.eduze.fyp.core.db.dao; import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import java.util.List; -public interface CameraConfigDAO { +public interface CameraConfigDAO extends AbstractDAO { + + List cameraGroups(); + + void addCameraGroup(CameraGroup cameraGroup); CameraConfig findById(int id); CameraConfig findByCameraId(int cameraId); - void save(CameraConfig cameraConfig); - void delete(CameraConfig cameraConfig); List list(); diff --git a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java index 9f7ee1ed..15e615b1 100644 --- a/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java +++ b/cramp-core/src/main/java/org/eduze/fyp/core/db/dao/CameraConfigDAOImpl.java @@ -23,19 +23,32 @@ package org.eduze.fyp.core.db.dao; import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.hibernate.HibernateException; import org.hibernate.Session; -import org.hibernate.SessionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -public class CameraConfigDAOImpl implements CameraConfigDAO { +public class CameraConfigDAOImpl extends AbstractDAOImpl implements CameraConfigDAO { private static final Logger logger = LoggerFactory.getLogger(CameraConfigDAO.class); - private SessionFactory sessionFactory; + + @Override + public List cameraGroups() { + Session session = this.sessionFactory.openSession(); + List cameraGroups = session.createQuery("from CameraGroup", CameraGroup.class) + .list(); + session.close(); + return cameraGroups; + } + + @Override + public void addCameraGroup(CameraGroup cameraGroup) { + this.save(cameraGroup); + } @Override public CameraConfig findById(int id) { @@ -62,15 +75,6 @@ public CameraConfig findByCameraId(int cameraId) { return null; } - @Override - public void save(CameraConfig cameraConfig) { - Session session = this.sessionFactory.openSession(); - session.beginTransaction(); - session.persist(cameraConfig); - session.getTransaction().commit(); - session.close(); - } - @Override public List list() { Session session = this.sessionFactory.openSession(); @@ -97,7 +101,7 @@ public void update(CameraConfig cameraConfig) { } @Override - public void delete(CameraConfig cameraConfig){ + public void delete(CameraConfig cameraConfig) { Session session = this.sessionFactory.openSession(); session.beginTransaction(); try { @@ -111,8 +115,4 @@ public void delete(CameraConfig cameraConfig){ session.close(); } } - - public void setSessionFactory(SessionFactory sessionFactory) { - this.sessionFactory = sessionFactory; - } } diff --git a/cramp-ui/src/main/resources/etc/spring.xml b/cramp-ui/src/main/resources/etc/spring.xml index c3d4cc54..886d42a2 100644 --- a/cramp-ui/src/main/resources/etc/spring.xml +++ b/cramp-ui/src/main/resources/etc/spring.xml @@ -85,6 +85,7 @@ org.eduze.fyp.api.model.CaptureStamp org.eduze.fyp.api.model.PointMapping org.eduze.fyp.api.model.CameraConfig + org.eduze.fyp.api.model.CameraGroup diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/RestServer.java b/cramp-web/src/main/java/org/eduze/fyp/web/RestServer.java index 446690c0..5ab133a4 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/RestServer.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/RestServer.java @@ -97,16 +97,16 @@ private void startRestServer() { ServletHolder servlet = new ServletHolder(servletContainer); context.addServlet(servlet, CONTEXT_PATH); - DefaultServlet defaultServlet = new DefaultServlet(); - ServletHolder servletHolder = new ServletHolder("default", defaultServlet); - URL path = this.getClass().getClassLoader().getResource("ng"); - try { - servletHolder.setInitParameter("resourceBase", path.toURI().toASCIIString()); - } catch (URISyntaxException e) { - logger.error("Error occurred", e); - } - servletHolder.setInitParameter("dirAllowed", "true"); - context.addServlet(servletHolder, "/"); +// DefaultServlet defaultServlet = new DefaultServlet(); +// ServletHolder servletHolder = new ServletHolder("default", defaultServlet); +// URL path = this.getClass().getClassLoader().getResource("ng"); +// try { +// servletHolder.setInitParameter("resourceBase", path.toURI().toASCIIString()); +// } catch (URISyntaxException e) { +// logger.error("Error occurred", e); +// } +// servletHolder.setInitParameter("dirAllowed", "true"); +// context.addServlet(servletHolder, "/"); jettyServer.setHandler(context); logger.debug("Starting REST server"); diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java index e39d9e77..6e6a2943 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/controllers/ConfigController.java @@ -18,9 +18,10 @@ */ package org.eduze.fyp.web.controllers; +import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.api.resources.Camera; -import org.eduze.fyp.api.model.CameraConfig; import org.eduze.fyp.web.resources.MapConfiguration; import org.eduze.fyp.web.resources.Status; import org.eduze.fyp.web.services.ConfigService; @@ -143,6 +144,29 @@ public Response getMap(@PathParam("cameraId") int cameraId) { return Response.status(200).entity(mapConfiguration).build(); } + @GET + @Path("/cameraGroups") + public Response getCameraGroups() { + try { + return Response.ok(configService.getCameraGroups()).build(); + } catch (Exception e) { + logger.error("Error occurred when obtaining camera configs", e); + return Response.status(500).build(); + } + } + + @POST + @Path("/cameraGroups") + public Response addCameraGroup(CameraGroup cameraGroup) { + try { + configService.addCameraGroup(cameraGroup); + return Response.ok().build(); + } catch (Exception e) { + logger.error("Error occurred when obtaining camera configs", e); + return Response.status(500).build(); + } + } + @GET @Path("/cameraConfigs") public Response getCameraConfigs() { diff --git a/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java b/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java index 56642b1a..7a45d2c9 100644 --- a/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java +++ b/cramp-web/src/main/java/org/eduze/fyp/web/services/ConfigService.java @@ -20,9 +20,12 @@ import org.eduze.fyp.api.AnalyticsEngine; import org.eduze.fyp.api.ConfigurationManager; +import org.eduze.fyp.api.Constants; import org.eduze.fyp.api.model.CameraConfig; +import org.eduze.fyp.api.model.CameraGroup; import org.eduze.fyp.api.model.Zone; import org.eduze.fyp.api.resources.Camera; +import org.eduze.fyp.api.util.Args; import org.eduze.fyp.api.util.ImageUtils; import org.eduze.fyp.core.db.dao.CameraConfigDAO; import org.eduze.fyp.core.db.dao.ZoneDAO; @@ -37,9 +40,6 @@ import java.util.List; import java.util.Map; -import static org.eduze.fyp.api.Constants.CAMERA_VIEW_HEIGHT; -import static org.eduze.fyp.api.Constants.CAMERA_VIEW_WIDTH; - public class ConfigService { private static final Logger logger = LoggerFactory.getLogger(ConfigService.class); @@ -71,8 +71,7 @@ public void addCameraConfig(CameraConfig cameraConfig) throws IOException { logger.debug("Adding camera configuration - {}", cameraConfig); // resizing camera view -// byte[] resized = ImageUtils.resize(cameraConfig.getView(), CAMERA_VIEW_WIDTH, CAMERA_VIEW_HEIGHT); - byte[] resized = cameraConfig.getView(); + byte[] resized = ImageUtils.resize(cameraConfig.getView(), Constants.CAMERA_VIEW_WIDTH, Constants.CAMERA_VIEW_HEIGHT); cameraConfig.setView(resized); cameraConfig.getPointMapping().setCameraConfig(cameraConfig); @@ -121,6 +120,36 @@ public Map getCameraConfigs() { return configurationManager.getCameraConfigs(); } + /** + * Get the available camera groups + * + * @return groups + */ + public List getCameraGroups() { + return cameraConfigDAO.cameraGroups(); + } + + /** + * Adds a new camera group + * + * @param cameraGroup camera group + */ + public void addCameraGroup(CameraGroup cameraGroup) { + Args.notNull(cameraGroup.getMap(), "Map"); + Args.notNull(cameraGroup.getName(), "Name"); + + logger.debug("Adding camera group: {}", cameraGroup); + + try { + byte[] resized = ImageUtils.resize(cameraGroup.getMap(), + Constants.MAP_IMAGE_WIDTH, Constants.MAP_IMAGE_HEIGHT); + cameraGroup.setMap(resized); + } catch (IOException e) { + logger.error("Unable to resize image for camera group: {}", cameraGroup, e); + } + cameraConfigDAO.addCameraGroup(cameraGroup); + } + public Zone addZone(Zone zone) { return zoneDAO.save(zone); } diff --git a/ngapp/src/app/resources/camera-config.ts b/ngapp/src/app/resources/camera-config.ts index 06d799f5..a64308ee 100644 --- a/ngapp/src/app/resources/camera-config.ts +++ b/ngapp/src/app/resources/camera-config.ts @@ -1,17 +1,37 @@ -import {PointMapping} from "./point-mapping"; -import {CameraView} from "./camera-view"; +import {PointMapping} from './point-mapping'; +import {CameraView} from './camera-view'; +import {CameraGroup} from './camera-group'; +import {Point} from "./point"; export class CameraConfig { cameraId: number; ipAndPort: string; pointMapping: PointMapping; cameraView: CameraView; + cameraGroup: CameraGroup; - constructor(cameraId: number, ipAndPort: string, pointMapping: PointMapping, cameraView: CameraView) { + constructor(cameraId: number, ipAndPort: string, pointMapping: PointMapping, cameraView: CameraView, cameraGroup: CameraGroup) { this.cameraId = cameraId; this.ipAndPort = ipAndPort; this.pointMapping = pointMapping; this.cameraView = cameraView; + this.cameraGroup = cameraGroup; + } + + public static fromJSON(config: any): CameraConfig { + const cameraId = config.cameraId; + const view = 'data:image/JPEG;base64,' + config.view; + const ipPort = config.ipAndPort; + const cameraGroup = CameraGroup.fromJSON(config.cameraGroup); + const cameraConfig = new CameraConfig(cameraId, ipPort, new PointMapping(), new CameraView(view), cameraGroup); + + for (const i in config.pointMapping.screenSpacePoints) { + const screenSpacePoint = new Point(config.pointMapping.screenSpacePoints[i].x, config.pointMapping.screenSpacePoints[i].y); + cameraConfig.pointMapping.screenSpacePoints.push(screenSpacePoint); + const worldSpacePoint = new Point(config.pointMapping.worldSpacePoints[i].x, config.pointMapping.worldSpacePoints[i].y); + cameraConfig.pointMapping.worldSpacePoints.push(worldSpacePoint); + } + return cameraConfig; } public toJSON(key: string): Object { @@ -19,7 +39,8 @@ export class CameraConfig { cameraId: this.cameraId, ipAndPort: this.ipAndPort, pointMapping: this.pointMapping, - view: this.cameraView.view.split(",")[1] + view: this.cameraView.view.split(',')[1], + cameraGroup: this.cameraGroup }; } } diff --git a/ngapp/src/app/resources/camera-group.ts b/ngapp/src/app/resources/camera-group.ts new file mode 100644 index 00000000..28c0d6d3 --- /dev/null +++ b/ngapp/src/app/resources/camera-group.ts @@ -0,0 +1,53 @@ +import {GlobalMap} from './global-map'; + +export class CameraGroup { + private _id: number; + private _name: string; + private _map: GlobalMap; + + constructor(id: number, name: string, map: GlobalMap) { + this.id = id; + this.name = name; + this.map = map; + } + + public static fromJSON(val: any) { + if (!val) { + return null; + } + + return new CameraGroup(val.id, val.name, GlobalMap.fromJSON(val.map)); + } + + get id(): number { + return this._id; + } + + set id(value: number) { + this._id = value; + } + + get name(): string { + return this._name; + } + + set name(value: string) { + this._name = value; + } + + get map(): GlobalMap { + return this._map; + } + + set map(value: GlobalMap) { + this._map = value; + } + + public toJSON() { + return { + id: this.id, + name: this.name, + map: this.map.toJSON() + }; + } +} diff --git a/ngapp/src/app/resources/global-map.ts b/ngapp/src/app/resources/global-map.ts index 6e6dc9e1..a1f38a85 100644 --- a/ngapp/src/app/resources/global-map.ts +++ b/ngapp/src/app/resources/global-map.ts @@ -25,12 +25,20 @@ export class GlobalMap { constructor(image: string) { this.image = image; - let obj = this; - let img = new Image(); + const obj = this; + const img = new Image(); img.onload = function () { obj.height = img.height; obj.width = img.width; }; img.src = image; } + + public static fromJSON(image: string) { + return new GlobalMap('data:image/png;base64,' + image); + } + + public toJSON() { + return this.image.split(',')[1]; + } } diff --git a/ngapp/src/app/services/config.service.ts b/ngapp/src/app/services/config.service.ts index 00507278..19ac4f52 100644 --- a/ngapp/src/app/services/config.service.ts +++ b/ngapp/src/app/services/config.service.ts @@ -23,9 +23,7 @@ import 'rxjs/add/operator/toPromise'; import {Zone} from '../resources/zone'; import {GlobalMap} from '../resources/global-map'; import {CameraConfig} from '../resources/camera-config'; -import {PointMapping} from '../resources/point-mapping'; -import {Point} from '../resources/point'; -import {CameraView} from '../resources/camera-view'; +import {CameraGroup} from "../resources/camera-group"; @Injectable() export class ConfigService { @@ -35,15 +33,27 @@ export class ConfigService { constructor(private http: HttpClient) { } + private static stringToNumberArray(str: string): number[] { + const arr: number[] = []; + for (const s of str.split(',')) { + arr.push(parseInt(s)); + } + return arr; + } + + private static handleError(error: any): Promise { + console.error('An error occurred', error); + return Promise.reject(error.message || error); + } + getCameraConfigs(): Promise { return this.http.get(this.baseUrl + 'cameraConfigs') .toPromise() .then(response => { - console.log(response); const configs: Map = response as Map; const cameraConfigs: CameraConfig[] = []; for (const key in configs) { - const cameraConfig: CameraConfig = ConfigService.toCameraConfig(configs[key]); + const cameraConfig: CameraConfig = CameraConfig.fromJSON(configs[key]); cameraConfigs.push(cameraConfig); } return cameraConfigs; @@ -59,15 +69,38 @@ export class ConfigService { }).catch(ConfigService.handleError); } + getCameraGroups(): Promise { + return this.http.get(this.baseUrl + 'cameraGroups') + .toPromise() + .then(response => { + const groups = response as CameraGroup[]; + console.log(groups); + const cameraGroups = []; + for (let g of groups) { + cameraGroups.push(CameraGroup.fromJSON(g)); + } + return cameraGroups; + }) + .catch(ConfigService.handleError); + } + + addCameraGroup(cameraGroup: CameraGroup): Promise { + return this.http.post(this.baseUrl + 'cameraGroups', cameraGroup) + .toPromise() + .then(response => { + console.log(response); + return true; + }) + .catch(ConfigService.handleError); + } + getMap(): Promise { return this.http.get(this.baseUrl + 'getMap') .toPromise() .then(response => { console.log(response); const views: Map = response as Map; - const base64: string = 'data:image/JPEG;base64,' + views['mapImage']; - - return new GlobalMap(base64); + return GlobalMap.fromJSON(views['mapImage']); }) .catch(ConfigService.handleError); } @@ -111,33 +144,4 @@ export class ConfigService { }) .catch(ConfigService.handleError); } - - private static toCameraConfig(config: any): CameraConfig { - const cameraId = config.cameraId; - const view = 'data:image/JPEG;base64,' + config.view; - const ipPort = config.ipAndPort; - const cameraConfig = new CameraConfig(cameraId, ipPort, new PointMapping(), new CameraView(view)); - - for (const i in config.pointMapping.screenSpacePoints) { - const screenSpacePoint = new Point(config.pointMapping.screenSpacePoints[i].x, config.pointMapping.screenSpacePoints[i].y); - cameraConfig.pointMapping.screenSpacePoints.push(screenSpacePoint); - const worldSpacePoint = new Point(config.pointMapping.worldSpacePoints[i].x, config.pointMapping.worldSpacePoints[i].y); - cameraConfig.pointMapping.worldSpacePoints.push(worldSpacePoint); - } - return cameraConfig; - } - - private static stringToNumberArray(str: string): number[] { - const arr: number[] = []; - for (const s of str.split(',')) { - arr.push(parseInt(s)); - } - return arr; - } - - private static handleError(error: any): Promise { - console.error('An error occurred', error); - return Promise.reject(error.message || error); - } - } diff --git a/ngapp/src/app/settings/point-mapping/point-mapping.component.html b/ngapp/src/app/settings/point-mapping/point-mapping.component.html index 562df39b..553ae91d 100644 --- a/ngapp/src/app/settings/point-mapping/point-mapping.component.html +++ b/ngapp/src/app/settings/point-mapping/point-mapping.component.html @@ -1,9 +1,45 @@
+ +
+
+
+
+
+

{{group.name}}

+ +
+
+
+
+ +
+
-

Configure point mappings for {{config.cameraId}}

+
+

Configure point mappings for {{config.cameraId}} {{config.cameraGroup.name}}

+
+
+ +
@@ -30,18 +66,20 @@

Configure point mappings for {{config.cameraId}}

-
- - - +
+ - - - - {{i+1}} - - + + + + + + + + {{i+1}} +
diff --git a/ngapp/src/app/settings/point-mapping/point-mapping.component.spec.ts b/ngapp/src/app/settings/point-mapping/point-mapping.component.spec.ts deleted file mode 100644 index 08d432a8..00000000 --- a/ngapp/src/app/settings/point-mapping/point-mapping.component.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Eduze - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and - * to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PointMappingComponent } from './point-mapping.component'; - -describe('PointMappingComponent', () => { - let component: PointMappingComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ PointMappingComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(PointMappingComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ngapp/src/app/settings/point-mapping/point-mapping.component.ts b/ngapp/src/app/settings/point-mapping/point-mapping.component.ts index 57678406..22021b82 100644 --- a/ngapp/src/app/settings/point-mapping/point-mapping.component.ts +++ b/ngapp/src/app/settings/point-mapping/point-mapping.component.ts @@ -18,11 +18,12 @@ */ import {Component, OnInit} from '@angular/core'; -import {ConfigService} from "../../services/config.service"; -import {CameraConfig} from "../../resources/camera-config"; -import {GlobalMap} from "../../resources/global-map"; -import {Point} from "../../resources/point"; -import {Message} from "../../lib/message"; +import {ConfigService} from '../../services/config.service'; +import {CameraConfig} from '../../resources/camera-config'; +import {GlobalMap} from '../../resources/global-map'; +import {Point} from '../../resources/point'; +import {Message} from '../../lib/message'; +import {CameraGroup} from '../../resources/camera-group'; @Component({ selector: 'app-point-mapping', @@ -32,6 +33,8 @@ import {Message} from "../../lib/message"; export class PointMappingComponent implements OnInit { cameraConfigs: CameraConfig[]; + cameraGroups: CameraGroup[]; + cameraGroup: CameraGroup; message: Message; globalMap: GlobalMap; @@ -39,6 +42,10 @@ export class PointMappingComponent implements OnInit { } ngOnInit(): void { + this.refresh(); + } + + private refresh() { this.configService.getCameraConfigs() .then(configs => { this.cameraConfigs = configs; @@ -46,10 +53,40 @@ export class PointMappingComponent implements OnInit { }) .catch(reason => console.error(reason)); + this.configService.getCameraGroups() + .then(groups => { + this.cameraGroups = groups; + }); + this.configService.getMap() .then(map => { this.globalMap = map; }); + + this.cameraGroup = new CameraGroup(null, null, null); + } + + public addCameraGroup() { + if (this.cameraGroup.name && this.cameraGroup.map) { + this.configService.addCameraGroup(this.cameraGroup) + .then(success => { + this.message = new Message('Camera group added successfully', Message.SUCCESS); + this.refresh(); + }); + } + } + + public mapChanged(files: FileList) { + if (files.length === 1) { + const reader = new FileReader(); + const cameraGroup = this.cameraGroup; + reader.onloadend = function () { + cameraGroup.map = GlobalMap.fromJSON(reader.result.split(',')[1]); + }; + reader.readAsDataURL(files.item(0)); + } else { + this.cameraGroup.map = null; + } } public mapClicked(event: MouseEvent, config: CameraConfig) { @@ -65,21 +102,33 @@ export class PointMappingComponent implements OnInit { } public save(config: CameraConfig) { - if (config.pointMapping.worldSpacePoints.length != 4 || config.pointMapping.screenSpacePoints.length != 4) { - this.message = new Message("You should select 4 points from each map", Message.ERROR); + console.log(config); + + if (config.pointMapping.worldSpacePoints.length !== 4 || config.pointMapping.screenSpacePoints.length !== 4) { + this.message = new Message('You should select 4 points from each map', Message.ERROR); + return; + } + + if (!config.cameraGroup) { + this.message = new Message('Please select camera group', Message.ERROR); return; } + console.log(config); this.configService.addCameraConfig(config) .then(success => { - this.message = new Message("Point mapping added successfully", Message.SUCCESS); + this.message = new Message('Point mapping added successfully', Message.SUCCESS); }).catch(reason => { - this.message = new Message("Point mapping couldn't be added", Message.ERROR); - }) + this.message = new Message('Point mapping couldn\'t be added', Message.ERROR); + }); } public clear(config: CameraConfig) { config.pointMapping.worldSpacePoints = []; config.pointMapping.screenSpacePoints = []; } + + public compare(group1: CameraGroup, group2: CameraGroup): boolean { + return group1 && group2 && group1.name === group2.name; + } } diff --git a/sense/LightWeightCamServer.py b/sense/LightWeightCamServer.py index 640da352..5bcf0de1 100644 --- a/sense/LightWeightCamServer.py +++ b/sense/LightWeightCamServer.py @@ -3,15 +3,15 @@ import cv2 from flask import Flask, jsonify +from OpenPersonDetector import OpenPersonDetector from PTEMapper import PTEMapper from Sense import Sense from Util import restEncodeImage from WorldSpaceTracker import WorldSpaceTracker from communication.ServerSession import ServerSession -from detectors.TFODDetector.TFODPersonDetector import TFODPersonDetector from experiments.AngleMapper import AngleMapper from experiments.Snapy import Snapy -from test_videos.VideoLoader import load_ntb_entrance +from test_videos.VideoLoader import load_video cam_server_instance = None @@ -115,7 +115,7 @@ def get_map_at(self, frame_time): "personCoordinates": result_coordinates } - cv2.imshow("output",frame) + cv2.imshow("output", frame) cv2.waitKey(1) return result @@ -142,9 +142,9 @@ def getMapFR(frame_time): if __name__ == "__main__": # Test snippet logging.basicConfig(level=logging.DEBUG) - cap, markers, map_markers = load_ntb_entrance() + cap, markers, map_markers = load_video("bia.pier2") - person_detector = TFODPersonDetector(preview=True) + person_detector = OpenPersonDetector(preview=True) position_mapper = PTEMapper(markers, map_markers) sense = Sense(person_detector, position_mapper, AngleMapper(position_mapper), WorldSpaceTracker(), Snapy()) diff --git a/sense/OpenPersonDetector.py b/sense/OpenPersonDetector.py index 2db06d5d..7f406da7 100755 --- a/sense/OpenPersonDetector.py +++ b/sense/OpenPersonDetector.py @@ -62,7 +62,6 @@ def initLibrary(preview, net_width, net_height): lock = RLock() - class OpenPersonDetector: ''' Person Detector and Leg Estimator based on OpenPose Project. @@ -120,7 +119,7 @@ def detectPersons(self, colour_frame, gray_frame): results = person_detector_api.detect(colour_frame) # Obtain scale factors - results_height, results_width = person_detector_api.getOutputHeight(), person_detector_api.getOutputWidth() + results_height, results_width = person_detector_api.getOutputHeight(), person_detector_api.getOutputHeight() frame_height, frame_width = colour_frame.shape[:2] scale_factor = frame_height / results_height @@ -162,8 +161,8 @@ def detectPersons(self, colour_frame, gray_frame): for j in range(len(_person)): # Do scale correction coordinate = _person[j] - coordinate[0] = coordinate[0] * scale_factor - coordinate[1] = coordinate[1] * scale_factor + # coordinate[0] = coordinate[0] * scale_factor + # coordinate[1] = coordinate[1] * scale_factor if coordinate[2] > 0: # In presence of point person_detection.tracked_points[coordinates_mapping[j]] = coordinate[:] @@ -231,6 +230,13 @@ def findAverage(names): neck = findAverage(["Neck"]) ankle = findAverage(["RAnkle", "LAnkle"]) knee = findAverage(["RKnee", "LKnee"]) + elbow = findAverage(["RElbow","LElbow"]) + + if hip is not None: + person_detection.hip_point = hip + + if elbow is not None: + person_detection.elbow_point = elbow if hip is not None and neck is not None: # Estimate leg Y using hip,neck,leg ratio @@ -241,7 +247,7 @@ def findAverage(names): # (person_detection.upper_body_bound[3] - person_detection.upper_body_bound[1])* 2) else: # Estimate leg y using bounds - logging.info("Poor Estimate of Leg Point") + logging.debug("Poor Estimate of Leg Point") person_detection.estimated_leg_point = (person_detection.central_point[0], person_detection.central_bound[1] + (person_detection.central_bound[3] - @@ -277,6 +283,8 @@ def __init__(self): self.central_bound = None # Boundary of central body of person (no hands and feet for X coordinate) self.upper_body_bound = None # Boundary of upper body of person self.central_point = None # Central point of person + self.hip_point = None + self.elbow_point = None self.leg_point = None # Average Feet point of person self.leg_count = None # Number of detected feet self.estimated_leg_point = None # Estimated feet point of person diff --git a/sense/test.py b/sense/test.py index 5006a941..f6f1d18f 100644 --- a/sense/test.py +++ b/sense/test.py @@ -3,19 +3,19 @@ import cv2 +from OpenPersonDetector import OpenPersonDetector from PTEMapper import PTEMapper from ScreenSpacePreview import ScreenSpacePreview from Sense import Sense from WorldSpaceTracker import WorldSpaceTracker -from detectors.TFODDetector.TFODPersonDetector import TFODPersonDetector from experiments.AngleMapper import AngleMapper from experiments.Snapy import Snapy -from test_videos.VideoLoader import load_ntb_entrance +from test_videos.VideoLoader import load_video def app(): - person_detector = TFODPersonDetector(preview=True) - (cap, markers, map_markers) = load_ntb_entrance() + person_detector = OpenPersonDetector(preview=False) + (cap, markers, map_markers) = load_video("bia.pier2") mapper = PTEMapper(markers, map_markers) diff --git a/sense/test_videos/Mapper.py b/sense/test_videos/Mapper.py new file mode 100644 index 00000000..05079a5e --- /dev/null +++ b/sense/test_videos/Mapper.py @@ -0,0 +1,83 @@ +import time + +import cv2 + +MODES = ("zone", "mapping") + + +def run_mapper(background_image, capture, scale): + map_points = [] + screen_points = [] + zones = [] + active_zone = [] + mode = 'zone' + + def mouse_clicked(event, x, y, flags, param): + if event == cv2.EVENT_LBUTTONDOWN: + if mode == "zone": + active_zone.append((x, y)) + print(active_zone) + elif mode == "mapping": + param.append((x, y)) + print(param) + elif event == cv2.EVENT_RBUTTONDOWN: + if mode == "zone": + if len(active_zone) > 0: + active_zone.pop() + print(active_zone) + elif mode == "mapping": + if len(param) > 0: + param.pop() + print(param) + + cv2.namedWindow("Screen") + cv2.namedWindow("Map") + cv2.setMouseCallback("Screen", mouse_clicked, param=screen_points) + cv2.setMouseCallback("Map", mouse_clicked, param=map_points) + + while True: + map_image = background_image.copy() + ret, frame = capture.read() + if not ret: + break + time.sleep(0.5) + + frame = cv2.resize(frame, (0, 0), fx=scale[0], fy=scale[1]) + + for point in screen_points: + cv2.drawMarker(frame, point, (0, 255, 0), thickness=2) + + for point in map_points: + cv2.drawMarker(map_image, point, (0, 255, 0), thickness=2) + + for point in active_zone: + cv2.drawMarker(map_image, point, (255, 255, 0), thickness=2) + + for zone in zones: + for point in zone: + cv2.drawMarker(map_image, (point[0], point[1]), (255, 0, 0), thickness=2) + + cv2.imshow("Screen", frame) + cv2.imshow("Map", map_image) + + key = cv2.waitKey(1) + if key & 0xFF == ord('q'): + break + elif key & 0xFF == ord('n'): + if len(active_zone) > 0: + zones.append(active_zone) + active_zone = [] + elif key & 0xFF == ord('m'): + mode = MODES[1] + elif key & 0xFF == ord('z'): + mode = MODES[0] + + print("Screen Space: ", screen_points) + print("World Space: ", map_points) + print("Zones: ", zones) + + +if __name__ == "__main__": + cap = cv2.VideoCapture("rtsp://admin:1234@10.201.81.105/cam/realmonitor?channel=01&subtype=01") + background_image = cv2.imread('counter.jpg') + run_mapper(background_image, cap, (1, 1)) diff --git a/sense/test_videos/VideoLoader.py b/sense/test_videos/VideoLoader.py index fbab5dee..f33cfde0 100755 --- a/sense/test_videos/VideoLoader.py +++ b/sense/test_videos/VideoLoader.py @@ -1,3 +1,6 @@ +import ast +import configparser + import cv2 ''' @@ -14,6 +17,41 @@ ''' +def load_video(name, scale=1): + config = configparser.ConfigParser() + config.read("test_videos/videos.conf") + + video = config[name] + cap = cv2.VideoCapture(video['path']) + + print(video['path']) + print(video['markers']) + print(video['map_markers']) + + markers = ast.literal_eval(video['markers']) + marker_scale = float(video.get('marker_scale', '1')) + markers = [x * (scale / marker_scale) for x in markers] + + map_markers = ast.literal_eval(video['map_markers']) + map_marker_scale = float(video.get('map_marker_scale', '1')) + map_markers = [x * (scale / map_marker_scale) for x in map_markers] + + return cap, markers, map_markers + + +def save_config(name, markers, map_markers, marker_scale=1, map_marker_scale=1): + config = configparser.ConfigParser() + config.read("videos.conf") + + config[name]['markers'] = str(markers) + config[name]['map_markers'] = str(map_markers) + config[name]['marker_scale'] = str(marker_scale) + config[name]['map_marker_scale'] = str(map_marker_scale) + + with open('test_videos/videos.conf', 'w') as configfile: + config.write(configfile) + + def loadOfficeRoomTest(): cap = cv2.VideoCapture("test_videos/test_office.mp4") cap.set(1, 300) diff --git a/sense/test_videos/videos.conf b/sense/test_videos/videos.conf new file mode 100644 index 00000000..bde34563 --- /dev/null +++ b/sense/test_videos/videos.conf @@ -0,0 +1,10 @@ +[bia.pier2] +path = test_videos/bia/pier2.mkv +markers = [] +map_markers = [] + +[office.room] +path = test_videos/test_office.mp4 +markers = [(615, 340), (13, 334), (175, 90), (430, 85)] +map_markers = [(400, 350), (200, 350), (200, 100), (400, 100)] +