Compare a BLOB image to images stored as ORDImage using SQL/MM Still Image

回眸只為那壹抹淺笑 提交于 2021-01-20 20:27:09

问题


I am using Oracle 11g r2.

I have a table that stores images as ORDImage :

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

And another temporary table that stores an image uploaded by a user as BLOB.

INSERT_TEMP (itemp_id integer, itemp_source blob)

I want to move the BLOB image to the PHOTOS table only if does not already exists, by comparing the two images. I need to use SQL/MM Still Image methods as ORDImageSignature methods are deprecated in Oracle 11g.

Here is the code :

 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;

The image is inserted as ORDImage successfully if I omit the comparison, else an exception is raised (sqlcode: 1, sqlerrm: User-defined Exception), using DBMS_UTILITY.FORMAT_ERROR_BACKTRACE it tells me the following :

ORA-06512: à "ORDSYS.SI_STILLIMAGE", ligne 27
ORA-06512: à "ORDSYS.SI_MKSTILLIMAGE1", ligne 6
ORA-06512: à "SURV.APP_CORE", ligne 212

line 212 is the line that checks 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;

It seems the problem is that it does not accept p.phot_source.source.localdata as parameter. Do you have any idea on how I can solve this ?

I have also tried :

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

Thank you !


回答1:


I finally came back to the problem, and get it to work.

The problem was simply that I had some null values in the ORDImage field...


I found my error by trying to store the StillImage object directly into my PHOTOS table :

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;

and then implementing the following minimal example :

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;
/ 

It was working fine when restricting the phot_id to 10, even by replacing p.phot_source2 with si_mkstillimage1(p.phot_source.source.localdata) (wich was causing the problem). But it failed when removing the phot_id restriction. So I finally understood that I had some null values in the phot_source column (ORDImage) that can cause the problem.

And indeed calling SI_StillImage() constructor with a null parameter leads to the following error message :

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

I removed all null values from the phot_source column and all is working fine now :)


To go further:

The downside of this is that it takes a very long time to do the comparison with all the images stored in the table (1155 seconds (arround 20 min) for 5000 photos). So I have tried to store images features directly into the table :

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

And then do the comparison like this :

-- 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;

But it gives the following error as it seems not possible to compare image features directly using the = operator :

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

I thought a solution would be to store image features as numeric values, but I read the entire documentation and I have not found any way to get any corresponding numeric value from an image feature.

Luckily, SI_score functions are provided for each image feature, so we can use the following to compare the images :

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;
/

I reduced the time from 1155 seconds (arround 20 min) to 226 seconds (less than 3 min) for 5000 images.

I know, it is still very slow, but I can't find another way to improve performances..., if anyone has an idea do not hesitate to share.




回答2:


Even this is more than a year old question I just found it when looking my own answers to another problem. But just wondering, Why you need to compare images by attributes, that is slow.

The best solution is to add an hash varchar field for the images and then compare those hashes. If same hash found then picture already exists. This can be example md5 hash. Of course you need to run once this procedure to already database existing pictures.

Use all kind of attributes of image to hash it, but not the file name itself: size, width, height, coloring all kind.

And when user tries to add a new image it takes those image attributes, hash them together and then compare that hash to other hashes in database and if it is a match then image already exists and ignore the insert.

Super fast version to compare images instead of "unpacking image"-method.



来源:https://stackoverflow.com/questions/9893809/compare-a-blob-image-to-images-stored-as-ordimage-using-sql-mm-still-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!