Сравнете BLOB изображение с изображения, съхранени като ORDImage, като използвате SQL/MM неподвижно изображение

Използвам Oracle 11g r2.

Имам таблица, която съхранява изображения като ORDImage:

PHOTOS (phot_id integer
        , phot_filename varchar2(256)
        , phot_source ordsys.ordimage)

И още една временна таблица, която съхранява изображение, качено от потребител като BLOB.

INSERT_TEMP (itemp_id integer, itemp_source blob)

Искам да преместя BLOB изображението в таблицата СНИМКИ само ако вече не съществува, като сравня двете изображения. Трябва да използвам методи за SQL/MM неподвижно изображение, тъй като методите ORDImageSignature са остарели в Oracle 11g.

Ето кода:

 declare
    [...]
 begin
    [...]
    -- get the blob from the temporary table (in_id passed as parameter)
    select itemp_source into l_img_blob from insert_temp where itemp_id = in_id;
    -- build the stillimage object from the blob
    l_img_obj := new si_stillimage(l_img_blob);
    -- get image features and build the featureList object
    l_avgcolor := new si_averagecolor(l_img_obj);
    l_colorhist := new si_colorhistogram(l_img_obj);
    l_poscolor := new si_positionalcolor(l_img_obj);
    l_texture := new si_texture(l_img_obj);
    l_featurelist := new SI_FeatureList(l_avgcolor, 1, l_colorhist, 1, l_poscolor, 1, l_texture, 1);
    -- check if a similar image already exists
    select count(*) into l_exist from photos p where SI_ScoreByFtrList(l_featurelist, SI_MkStillImage1(p.phot_source.source.localdata)) = 0;
    if (l_exist > 0) then
       out_message := app_util.get_translated_message('ERR_SIMILAR_PHOTO_ALREADY_EXISTS');
    else
       /* here the blob is inserted into the PHOTOS table as ORDImage successfully */
       out_message := app_util.get_translated_message('SUC_PHOTO_INSERTED');
    end if;
 end;

Изображението се вмъква успешно като ORDImage, ако пропусна сравнението, в противен случай се създава изключение (sqlcode: 1, sqlerrm: дефинирано от потребителя изключение), използвайки DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ми казва следното:

ORA-06512: в "ORDSYS.SI_STILLIMAGE", ред 27
ORA-06512: в "ORDSYS.SI_MKSTILLIMAGE1", ред 6
ORA-06512: в "SURV.APP_CORE", ред 212

ред 212 е редът, който проверява дали вече съществува подобно изображение:

 select count(*) into l_exist
 from photos p 
 where SI_ScoreByFtrList(l_featurelist, SI_MkStillImage1(p.phot_source.source.localdata)) = 0;

Изглежда проблемът е, че не приема p.phot_source.source.localdata като параметър. Имате ли идея как мога да разреша това?

Опитах също:

 select count(*) into l_exist 
 from photos p
 where l_featurelist.si_score(new si_stillimage1(p.phot_source.source.localdata)) = 0;

Благодаря ти !


person Yann39    schedule 27.03.2012    source източник


Отговори (2)


Най-накрая се върнах към проблема и го накарах да работи.

Проблемът беше просто, че имах някои null стойности в полето ORDImage...


Открих грешката си, опитвайки се да съхраня обекта StillImage директно в моята таблица СНИМКИ:

alter table PHOTOS add phot_source2 SI_Stillimage;
update photos p set p.phot_source2 = si_stillimage(p.phot_source.source.localData) where p.phot_id < 10;

и след това прилагане на следния минимален пример:

DECLARE
    l_img_obj   si_stillimage;
    l_avgcolor  si_averagecolor;
    l_colorhist si_colorhistogram;
    l_poscolor  si_positionalcolor;
    l_texture   si_texture;
    l_featurelist   si_featurelist;
    l_blob      BLOB;
    l_exist     INTEGER;
BEGIN
    -- get the blob from the ordimage
    SELECT p.phot_source.source.localdata
    INTO l_blob FROM photos p
    WHERE phot_id = 2;
    -- build the stillimage object from the blob
    l_img_obj := NEW si_stillimage(l_blob);
    -- get image features and build the featureList object
    l_avgcolor    := NEW si_averagecolor(l_img_obj);
    l_colorhist   := NEW si_colorhistogram(l_img_obj);
    l_poscolor    := NEW si_positionalcolor(l_img_obj);
    l_texture     := NEW si_texture(l_img_obj);
    l_featurelist := NEW si_featurelist(l_avgcolor, 1, l_colorhist, 1, l_poscolor, 1, l_texture, 1);
    -- check if a similar image is found in the table
    SELECT 1
    INTO l_exist
    FROM photos p
    WHERE si_scorebyftrlist(l_featurelist, p.phot_source2) = 0
    AND phot_id < 10
    AND rownum = 1;
    -- show message if at least one similar photo has been found
    IF (l_exist = 1) THEN       
        dbms_output.put_line('A similar photo has been found');
    END IF;
END;
/ 

Работеше добре при ограничаване на phot_id до 10, дори чрез замяна на p.phot_source2 с si_mkstillimage1(p.phot_source.source.localdata) (което причиняваше проблема). Но не успя при премахване на ограничението phot_id. Така най-накрая разбрах, че имам някои null стойности в колоната phot_source (ORDImage), които могат да причинят проблема.

И наистина извикването на SI_StillImage() конструктор с null параметър води до следното съобщение за грешка:

ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "ORDSYS.SI_STILLIMAGE", line 27
ORA-06512: at "ORDSYS.SI_MKSTILLIMAGE1", line 6
ORA-06512: at line 24

Премахнах всички null стойности от колоната phot_source и вече всичко работи добре :)


За да отидете по-далеч:

Недостатъкът на това е, че отнема много време, за да се направи сравнение с всички изображения, съхранени в таблицата (1155 секунди (около 20 минути) за 5000 снимки) . Така че се опитах да съхраня функциите на изображенията директно в таблицата:

alter table photos add (
    phot_averagecolor si_averagecolor,
    phot_colorhistogram si_colorhistogram,
    phot_positionalcolor si_positionalcolor,
    phot_texture si_texture
)

update photos p set
    p.phot_averagecolor = si_averagecolor(si_stillimage(p.phot_source.source.localData)),
    p.phot_colorhistogram = si_colorhistogram(si_stillimage(p.phot_source.source.localData)),
    p.phot_positionalcolor = si_positionalcolor(si_stillimage(p.phot_source.source.localData)),
    p.phot_texture = si_texture(si_stillimage(p.phot_source.source.localData))
where p.phot_id < 10

И след това направете сравнението по следния начин:

-- get the blob from the ordimage
SELECT p.phot_source.source.localdata
INTO l_blob FROM photos p
WHERE phot_id = 2;
-- build the stillimage object from the blob
l_img_obj := NEW si_stillimage(l_blob);
-- get image features and build the featureList object
l_avgcolor    := si_averagecolor(l_img_obj);
l_colorhist   := si_colorhistogram(l_img_obj);
l_poscolor    := si_positionalcolor(l_img_obj);
l_texture     := si_texture(l_img_obj);
l_featurelist := NEW si_featurelist(l_avgcolor, 1, l_colorhist, 1, l_poscolor, 1, l_texture, 1);
-- check if a similar image is found in the table
SELECT 1
INTO l_exist
FROM photos p
WHERE p.phot_averagecolor = l_avgcolor
AND p.phot_colorhistogram = l_colorhist
AND p.phot_positionalcolor = l_poscolor
AND p.phot_texture = l_texture
AND p.phot_id < 10
AND rownum = 1;

Но дава следната грешка, тъй като изглежда не е възможно да се сравнят характеристиките на изображението директно с помощта на оператора =:

ORA-22901: cannot compare VARRAY or LOB attributes of an object type
ORA-06512: at line 24

Мислех, че решение ще бъде да съхранявам характеристиките на изображението като числови стойности, но прочетох целия документация и не намерих никакъв начин да получа съответстваща числова стойност от функция на изображение.

За щастие, SI_score функции са предоставени за всяка функция на изображението, така че можем да използваме следното, за да сравним изображенията:

DECLARE
    l_img_obj   si_stillimage;
    l_blob      BLOB;
    l_exist     INTEGER;
BEGIN
    -- get the blob from the ordimage
    SELECT p.phot_source.source.localdata
    INTO l_blob FROM photos p
    WHERE phot_id = 2;
    -- build the stillimage object from the blob
    l_img_obj := NEW si_stillimage(l_blob);
    -- check if a similar image is found in the table
    SELECT 1
    INTO l_exist
    FROM photos p
    WHERE p.phot_averagecolor.SI_Score(l_img_obj) = 0
    AND p.phot_colorhistogram.SI_Score(l_img_obj) = 0
    AND p.phot_positionalcolor.SI_Score(l_img_obj) = 0
    AND p.phot_texture.SI_Score(l_img_obj) = 0
    AND rownum = 1;
    -- show message
    dbms_output.put_line(l_count || ' similar photo(s) found');
END;
/

Намалих времето от 1155 секунди (около 20 минути) на 226 секунди (по-малко от 3 минути) за 5000 изображения.

Знам, че все още е много бавен, но не мога да намеря друг начин за подобряване на представянето..., ако някой има идея, не се колебайте да сподели.

person Yann39    schedule 27.07.2012

Дори това да е въпрос отпреди повече от година, току-що го намерих, докато търсих собствените си отговори на друг проблем. Но просто се чудя защо трябва да сравнявате изображения по атрибути, това е бавно.

Най-доброто решение е да добавите поле за хеш varchar за изображенията и след това да сравните тези хешове. Ако се намери същият хеш, тогава картината вече съществува. Това може да бъде примерен md5 хеш. Разбира се, трябва да стартирате веднъж тази процедура към вече съществуващи снимки в база данни.

Използвайте всички видове атрибути на изображението, за да го хеширате, но не и самото име на файла: размер, ширина, височина, оцветяване от всякакъв вид.

И когато потребителят се опита да добави ново изображение, той взема тези атрибути на изображението, хешира ги заедно и след това сравнява този хеш с други хешове в базата данни и ако съвпада, изображението вече съществува и игнорира вмъкването.

Супер бърза версия за сравняване на изображения вместо метода "разопаковане на изображението".

person user2882624    schedule 24.10.2013
comment
Благодаря ви за отговора, все още се чудя защо мозъкът ми не се е сетил за това тогава. Най-накрая решихме да не правим сравнение на изображения, но ако трябва да се върна към проблема, със сигурност ще използвам това решение. - person Yann39; 24.10.2013