簡體   English   中英

如何在SQL查詢中按相似度查找圖像?

[英]How to find images by similarity, inside a SQL query?

我想處理圖像以在每個區域中建立9個區域,然后找到每個區域的平均顏色,然后將其保存到如下所示的char字段中:

255,255,255,255,255,255,107,195,305

然后,要查找與給定圖像相似的所有圖像,我必須計算每對顏色之間的距離(相對於相同區域),例如:

這些圖像之間的區別是:

255,255,255,255,255,255,107,195,305
255,255,255,255,255,255,107,195,304

這些圖像之間的差異是3:

255,255,255,255,255,255,105,195,305
255,255,255,255,255,255,107,195,304

我的問題是如何執行此類查詢,並按相似性對其進行排序? 該字段只是一個字符串,其值用逗號分隔。

這樣的查詢是否可能很快? 還是我應該尋找其他方法? 我們正在談論數千張圖片

編輯:作為@therealsix,一種選擇是將每個平均顏色值放入單獨的列中。

一種更“ SQLey”的方法可能是使用具有2個表的標准化數據庫方法:

Image(ImageID int, ... other columns as required ...)
ImageZone(ImageID int, ZoneIndex int, ColourValue int, ...)

因此,對於您的示例,您可能有

ImageID   ZoneIndex   ColourValue
-------   ---------   -----------
  1          1          255
  1          2          255
  ...
  1          9          304
  2          1          255
  ...
  2          9          305

然后,為了獲得距離,類似(我是SQL Server專家,但這應該可以很容易地翻譯成MySQL):

 SELECT
    Candidate.ImageID,
    Candidate.ImageFile, /* or whatever... */
    Scores.Difference
 FROM
 (
   SELECT
      Original.ImageID AS OriginalID,
      Candidate.ImageID AS CandidateID,
      SUM(ABS(Original.ColourValue - Candidate.ColourValue)) AS Difference
   FROM ImageZone AS Original
   INNER JOIN ImageZone AS Candidate
     ON (Original.ImageID <> Candidate.ImageID)
     ON (Original.ZoneIndex = Candidate.ZoneIndex)
 ) AS Scores
 INNER JOIN Image AS Candidate ON (Scores.CandidateID = Candidate.ImageID)
 WHERE Scores.OriginalID = 1 /* or the image ID you want to look up */
 ORDER BY Difference

因此,內部查詢會為每個候選區域創建一行,例如(O =原始,C =候選):

 O.ImageID  O.ZoneIndex  O.ColourValue  C.ImageID  C.ZoneIndex  C.ColourValue
 ---------  -----------  -------------  ---------  -----------  -------------
    1           1           255            2            1           255
    ... then ...
    1           9           305            2            9           304
    1           1           255            3            1            99
    ... then ...
    99          9           100           98            9            99

然后匯總為總差異:

 OriginalID  CandidateID  Difference
 ----------  -----------  ----------
    1            2            1
    1            3           10
    ...
    99          98          500

然后,您從此虛擬表中選擇,僅在OriginalID為1的情況下,然后將其重新連接到原始Image表,以獲取最低“差異”分數(在本例中為2)所需的詳細信息。

恕我直言,這是一種更加簡潔的數據庫設計(如果以后使用更多區域等,則非常適合)。

實際上,從它的聲音來看,您正在嘗試做一種序列比對的形式。 為此,使用了一系列算法來比較基因序列:

序列比對

我想您正在尋找類似的東西...

http://kodisha.net/color-names/?color=FD464A

注意右邊的色差...

Color Difference: 1.84568861095

http://en.wikipedia.org/wiki/Color_difference

如果同時存在大量用戶,則對1000多個行運行此查詢肯定會殺死您的服務器。

我建議使用mysql函數與您隨機給定的圖像進行比較。 首先讓我們創建一個簡單的示例表

DROP TABLE IF EXISTS images;

CREATE TABLE images (
  id         INTEGER AUTO_INCREMENT PRIMARY KEY,
  rgb_values VARCHAR(255)
);

現在讓我們定義將在查詢中使用的函數。 第一個啟用用於在任何定界符上分割字符串並通過索引返回所需的元素:

DROP FUNCTION SPLIT_STR;

CREATE FUNCTION SPLIT_STR(
  x VARCHAR(255),
  delim VARCHAR(12),
  pos INT
)
RETURNS VARCHAR(255)
RETURN
  REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
  LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
  delim, '')
;

接下來,我們定義一個函數來根據您的算法(或您要使用的與此算法有關的任何算法)計算圖像差異:

DROP FUNCTION IMAGE_DIFF;

CREATE FUNCTION IMAGE_DIFF(
  from_val VARCHAR(255),
  to_val VARCHAR(255)
)
RETURNS INTEGER(4)
RETURN
  ABS((SPLIT_STR(to_val, ',', 1) - SPLIT_STR(from_val, ',',1))) +
  ABS((SPLIT_STR(to_val, ',', 2) - SPLIT_STR(from_val, ',',2))) +
  ABS((SPLIT_STR(to_val, ',', 3) - SPLIT_STR(from_val, ',',3))) +
  ABS((SPLIT_STR(to_val, ',', 4) - SPLIT_STR(from_val, ',',4))) +
  ABS((SPLIT_STR(to_val, ',', 5) - SPLIT_STR(from_val, ',',5))) +
  ABS((SPLIT_STR(to_val, ',', 6) - SPLIT_STR(from_val, ',',6))) +
  ABS((SPLIT_STR(to_val, ',', 7) - SPLIT_STR(from_val, ',',7))) +
  ABS((SPLIT_STR(to_val, ',', 8) - SPLIT_STR(from_val, ',',8))) +
  ABS((SPLIT_STR(to_val, ',', 9) - SPLIT_STR(from_val, ',',9)))
;

讓我們創建一些示例數據:

INSERT INTO images(rgb_values) VALUES ("237,128,73,69,35,249,199,183,178");
INSERT INTO images(rgb_values) VALUES ("39,212,164,170,202,49,93,77,145");
INSERT INTO images(rgb_values) VALUES ("28,242,83,167,92,161,115,38,108");
INSERT INTO images(rgb_values) VALUES ("72,81,73,2,77,109,177,204,120");
INSERT INTO images(rgb_values) VALUES ("165,149,106,248,39,26,167,237,139");
INSERT INTO images(rgb_values) VALUES ("183,40,156,131,120,19,71,88,69");
INSERT INTO images(rgb_values) VALUES ("138,136,112,36,69,245,130,196,24");
INSERT INTO images(rgb_values) VALUES ("1,194,153,107,16,102,164,154,74");
INSERT INTO images(rgb_values) VALUES ("172,161,17,179,140,244,23,219,115");
INSERT INTO images(rgb_values) VALUES ("166,151,48,62,154,227,44,21,201");
INSERT INTO images(rgb_values) VALUES ("118,73,212,180,150,64,254,177,68");
INSERT INTO images(rgb_values) VALUES ("119,220,226,254,14,175,123,11,134");
INSERT INTO images(rgb_values) VALUES ("118,93,238,31,77,36,105,151,216");
INSERT INTO images(rgb_values) VALUES ("123,108,177,136,9,24,119,175,88");
INSERT INTO images(rgb_values) VALUES ("11,207,12,215,215,80,101,213,143");
INSERT INTO images(rgb_values) VALUES ("132,158,46,188,7,245,241,126,214");
INSERT INTO images(rgb_values) VALUES ("167,238,186,86,109,164,219,199,238");
INSERT INTO images(rgb_values) VALUES ("216,93,139,246,153,39,226,152,143");
INSERT INTO images(rgb_values) VALUES ("98,229,7,203,230,224,57,154,252");
INSERT INTO images(rgb_values) VALUES ("7,95,145,120,35,6,116,240,64");
INSERT INTO images(rgb_values) VALUES ("45,194,172,223,96,168,18,4,215");
INSERT INTO images(rgb_values) VALUES ("243,161,214,235,134,190,207,63,127");
INSERT INTO images(rgb_values) VALUES ("74,189,249,85,148,169,65,3,81");
INSERT INTO images(rgb_values) VALUES ("46,113,191,20,108,139,60,249,6");
INSERT INTO images(rgb_values) VALUES ("153,246,189,175,5,125,9,197,160");
INSERT INTO images(rgb_values) VALUES ("202,248,23,59,81,175,197,180,114");
INSERT INTO images(rgb_values) VALUES ("73,136,252,137,222,197,118,64,69");
INSERT INTO images(rgb_values) VALUES ("172,224,251,32,154,175,201,33,14");
INSERT INTO images(rgb_values) VALUES ("141,126,112,12,45,214,243,127,49");
INSERT INTO images(rgb_values) VALUES ("116,155,23,205,62,235,111,136,205");

然后使用我們新定義的函數針對要與之進行比較的圖像運行查詢:

mysql> SELECT id
    ->      , image_diff(rgb_values, '255,191,234,123,85,23,255,255,255') rgb_diff
    ->   FROM images
    ->  ORDER BY 2 DESC;
+----+----------+
| id | rgb_diff |
+----+----------+
| 19 |     1150 |
| 10 |     1148 |
|  3 |     1122 |
| 27 |     1094 |
|  9 |     1070 |
| 15 |     1069 |
| 23 |     1061 |
| 21 |     1059 |
|  7 |     1034 |
| 12 |     1024 |
| 24 |     1022 |
| 30 |     1016 |
| 29 |      989 |
| 28 |      962 |
|  2 |      947 |
|  4 |      933 |
| 16 |      893 |
|  6 |      885 |
|  8 |      875 |
| 20 |      848 |
| 25 |      835 |
| 26 |      815 |
|  1 |      777 |
| 22 |      758 |
| 14 |      745 |
| 11 |      706 |
| 18 |      683 |
|  5 |      656 |
| 13 |      645 |
| 17 |      494 |
+----+----------+
30 rows in set (0.01 sec)

好的,因此您的表格圖片具有ID和9個單獨的顏色字段-color1到color 9

SELECT a.id, b.id, ( ABS( a.color1 - b.color ) + ABS( a.color2 + b.color2 ) + ABS( a.color3 + b.color3 ) + ... ) AS difference
FROM images AS a
JOIN images AS b
WHERE a.id > b.id
ORDER BY difference

這可能相當有效,您必須嘗試一下。

在我看來,這個問題似乎不是一個序列比較問題,而是一個地理問題。

我想您想在第9維點集中找到附近的點。

查看有關空間數據庫如何使用R樹進行聚類有效搜索的文章(例如,附近的點正是您想要的。): 空間數據庫的增量距離聯接算法 (單擊“緩存”鏈接)

真正的問題是我不知道支持9維的空間數據庫。 我能想到的唯一技巧是三點地理三元組(A,B,C)。

為了使我的觀點更加清楚。 讓我們來看看您的數據:

這些圖像之間的差異是3:

 255,255,255,255,255,255,105,195,305 255,255,255,255,255,255,107,195,304 

我們可以將以上兩行看作是9維世界中的2個點(分別稱為ab )。

這9個數字是它們的坐標( a1,a2,...,a9b1,b2,...,b9 )。

而“差異”就是它們的距離: Sum(|ai-bi|) 定義距離的方法有很多,這是常見的方法之一。 不是歐幾里得距離,而是相似的。 而且計算起來要快一些。

現在,如果您真的要擁有成千上萬的圖像,那么計算所有數百萬(或數萬億)的距離將是一個非常緩慢的過程。 如果您一次只需要比較幾千個,那么我想您已經有兩個答案可以了。

但是,如果您真的想查找相似的圖像並存儲大量(如數十萬個)圖像,則R樹或空間數據庫使用的其他索引會更好。 R樹並不是什么神奇的東西,它只是專門針對此類多維數據的索引。

如果找不到支持這么多維度的空間數據庫,則不確定如何創建自己的解決方案。

一種想法是將9個數字分成3個三胞胎。 每個三元組將是一個3維點。 因此,您的每個圖像都將存儲為三個3D地理點。 那么,兩幅圖像之間的差異將是三個(地理)距離的總和。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM