ImageController.java 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package com.mokasz.image_pool.restful.controller;
  2. import com.mokasz.image_pool.restful.entity.ImageQueryResponse;
  3. import com.mokasz.image_pool.restful.entity.ImageSaveResponse;
  4. import org.bytedeco.javacpp.Loader;
  5. import org.bytedeco.opencv.opencv_java;
  6. import org.opencv.core.Mat;
  7. import org.opencv.core.MatOfByte;
  8. import org.opencv.img_hash.PHash;
  9. import org.opencv.imgcodecs.Imgcodecs;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.jdbc.core.JdbcTemplate;
  14. import org.springframework.jdbc.core.RowMapper;
  15. import org.springframework.web.bind.annotation.PostMapping;
  16. import org.springframework.web.bind.annotation.RequestMapping;
  17. import org.springframework.web.bind.annotation.RequestParam;
  18. import org.springframework.web.bind.annotation.RestController;
  19. import org.springframework.web.multipart.MultipartFile;
  20. import java.io.IOException;
  21. import java.sql.ResultSet;
  22. import java.sql.SQLException;
  23. import java.util.List;
  24. @RestController
  25. @RequestMapping("/service/image")
  26. public class ImageController {
  27. // 静态初始化 OpenCV
  28. static {
  29. Loader.load(opencv_java.class);
  30. }
  31. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  32. private final PHash pHash = PHash.create();
  33. @Autowired
  34. private JdbcTemplate jdbcTemplate;
  35. private void calculate (byte[] fileBytes, float[] vector)
  36. {
  37. Mat input = Imgcodecs.imdecode(new MatOfByte(fileBytes), Imgcodecs.IMREAD_UNCHANGED);
  38. Mat output = new Mat();
  39. pHash.compute(input, output);
  40. transformMatToArrayOfFloat(output, vector);
  41. }
  42. private void transformMatToArrayOfFloat (Mat mat, float[] floats) {
  43. if (mat.width() != 8 || mat.height() != 1) throw new RuntimeException("Matrix dimension is not 1x8");
  44. for (int i = 0; i < 8; i++) {
  45. floats[i] = (float) mat.get(0, i)[0];
  46. }
  47. }
  48. private void transformBinaryArray (float[] octets, float[] binaries) {
  49. for (int i = 0; i < 8; i++) {
  50. for (int j = 0; j < 8; j++) {
  51. int v = (int) octets[i];
  52. binaries[i * 8 + j] = (v & (1 << j)) >> j;
  53. }
  54. }
  55. }
  56. private String getReadableHash (float[] floats) {
  57. StringBuilder stringBuilder = new StringBuilder();
  58. for (int i = 0; i < 8; i++) {
  59. stringBuilder.append(String.format("%02X", (int) floats[i]));
  60. }
  61. return stringBuilder.toString();
  62. }
  63. @PostMapping("/store")
  64. public ImageSaveResponse store (@RequestParam("file") MultipartFile file) throws IOException {
  65. logger.info("File size: {} Name: {}", file.getSize(), file.getOriginalFilename());
  66. float[] floats = new float[8];
  67. calculate(file.getBytes(), floats);
  68. float[] binaries = new float[64];
  69. transformBinaryArray(floats, binaries);
  70. logger.info("{}", binaries);
  71. String hash = getReadableHash(floats);
  72. int updated = jdbcTemplate.update("INSERT INTO image_pool.image_hash VALUES (?, NOW(), ?) ON CONFLICT (hash) DO NOTHING", hash, binaries);
  73. logger.info("{}", updated);
  74. return new ImageSaveResponse(hash, 1L);
  75. }
  76. @PostMapping("/match")
  77. public List<ImageQueryResponse> match (@RequestParam("file") MultipartFile file, @RequestParam("pool") String pool) throws IOException {
  78. logger.info("Not implemented pool: {}", pool);
  79. float[] floats = new float[8];
  80. calculate(file.getBytes(), floats);
  81. float[] binaries = new float[64];
  82. transformBinaryArray(floats, binaries);
  83. logger.info("{}", floats);
  84. List<ImageQueryResponse> list = jdbcTemplate.query("SELECT hash, l2_distance(?::FLOAT2[], vector) AS similarity FROM image_pool.image_hash ORDER BY vector <-> ?::FLOAT2[] DESC LIMIT 2",
  85. (ResultSet rs, int rowNum) -> new ImageQueryResponse(rs.getFloat("similarity"), rs.getString("hash")), binaries, binaries);
  86. return list;
  87. }
  88. }