2015-01-13 235 views
2

我們想知道是否有可能執行類似附圖的操作。GIF或PNG圖像上的顏色檢測

我們在我們的網站上有一個實時氣象雷達,投影在谷歌地圖頁面上,更新週期爲5分鐘。

這是什麼想法?

我們想爲我們的遊客探測到「沉重的」風暴,並用方箱或其他東西來突出它們。如果可能的話,我們想用PHP製作這個系統。我認爲最好的方法是檢測顏色或其他東西?

附爲例,我們用Photoshop繪製的圖片:

我們希望有人能幫助我們,所以我們可以開始使用的東西!

original image heavy storms highlighted with square boxes

回答

2

我對此進行了另一次嘗試,使用我在C中編寫的一些Connected Component Analysis軟件。它很容易在任何OS X/Linux/Windows機器上編譯。

所以,這裏是腳本:

#!/bin/bash 

# Make red areas white and all else black for blob analysis 
convert http://i.stack.imgur.com/qqein.png \ 
    -fuzz 50%        \ 
    -fill white +opaque red     \ 
    -fill black -opaque red -colorspace gray -negate -depth 16 weather.pgm 

# Run Connected Component Analysis to find white blobs and their areas and bounding boxes 
./cca <weather.pgm> /dev/null 2> info.txt 

# Find blobs with more than 100 pixels 
while read a b ;do 
    draw="$draw -draw \"rectangle $a $b\" " 
done < <(awk '/Area/{area=$5+0;if(area>100)print $7,$8}' info.txt) 

# Now draw the rectangles on top of the source image 
eval convert http://i.stack.imgur.com/qqein.png -strokewidth 2 -stroke red -fill none "$draw" result.png 

文件weather.pgm出來是這樣的:

enter image description here

部分輸出的cca程序

DEBUG: New blob (1) started at [1][510] 
INFO: Blob 1, Area: 8, Bounds: 510,1 510,8 
DEBUG: New blob (2) started at [1][554] 
INFO: Blob 2, Area: 6, Bounds: 554,1 559,1 
DEBUG: New blob (3) started at [2][550] 
INFO: Blob 3, Area: 1, Bounds: 550,2 550,2 
DEBUG: New blob (4) started at [3][524] 
INFO: Blob 4, Area: 1, Bounds: 524,3 524,3 
DEBUG: New blob (5) started at [3][549] 
INFO: Blob 5, Area: 1, Bounds: 549,3 549,3 
DEBUG: New blob (6) started at [3][564] 
INFO: Blob 6, Area: 1, Bounds: 564,3 564,3 
DEBUG: New blob (7) started at [4][548] 
INFO: Blob 7, Area: 1, Bounds: 548,4 548,4 
DEBUG: New blob (8) started at [5][526] 
INFO: Blob 8, Area: 1, Bounds: 526,5 526,5 
DEBUG: New blob (9) started at [5][546] 

在腳本的最後convert命令被稱爲是這樣的:

convert http://i.stack.imgur.com/qqein.png -strokewidth 2 -stroke red -fill none \ 
    -draw 'rectangle 930,125 958,142' -draw 'rectangle 898,138 924,168'    \ 
    -draw 'rectangle 822,143 846,172' -draw 'rectangle 753,167 772,175'    \ 
    -draw 'rectangle 658,181 758,215' -draw 'rectangle 759,186 803,197'    \ 
    -draw 'rectangle 340,223 372,267' -draw 'rectangle 377,259 429,294'    \ 
    -draw 'rectangle 977,281 988,357' -draw 'rectangle 705,321 751,351'    \ 
    -draw 'rectangle 624,376 658,412' -draw 'rectangle 357,485 380,499' result.png 

,結果是這樣的:

enter image description here

cca.c程序是這樣的:

/******************************************************************************* 
File: cca.c 
Author: Mark Setchell 

Description: 
Connected Components Analyser and Labeller - see algorithm at 
http://en.m.wikipedia.org/wiki/Connected-component_labeling#One-pass_version 

Algorithm 
========= 

1. Start from the first pixel in the image. Set "curlab" (short for "current label") to 1. Go to (2). 
2. If this pixel is a foreground pixel and it is not already labelled, then give it the label "curlab" and add it as the first element in a queue, then go to (3). If it is a background pixel, then repeat (2) for the next pixel in the image. 

3. Pop out an element from the queue, and look at its neighbours (based on any type of connectivity). If a neighbour is a foreground pixel and is not already labelled, give it the "curlab" label and add it to the queue. Repeat (3) until there are no more elements in the queue. 
4. Go to (2) for the next pixel in the image and increment "curlab" by 1. 

CurrentLabel=1 
for all pixels in image 
    if this is a foreground pixel 
     if this pixel is not already labelled 
     label this pixel with Currentlabel 
     add this pixel to queue 
     while there are items in the queue 
      pop item from queue 
      for all 4-connected or 8-connected neighbours of this item 
       if neighbour is foreground and is not already labelled 
        label this neighbour with Currentlabel 
        add this neighbour to the queue 
       endif 
      endfor 
     endwhile 
     increment Currentlabel 
     endif 
    else 
     label as background in output image 
    endif 
endfor 

Usage 
===== 

Usage: cca [-c 4|8] <Binarized16BitPGMFile> Binarized16BitPGMFile 

where "-c" specifies whether pixels must be 4- or 8-connected to be considered 
as parts of same object. By default 4-connectivity is assumed. 

Files can be prepared for this program with ImageMagick as follows: 

    convert YourImage.[jpg|bmp|png|tif] \ 
      -colorspace gray   \ 
      -threshold 50%    \ 
      -depth 16     \ 
      [-negate]     \ 
      FileForAnalysis.pgm 

This program expects the background pixels to be black and the objects to be 
white. If your image is inverted relative to this, use the "-negate" option. 

On OSX, run and view results with ImageMagick like this: 

    cca < test1.pgm | convert PGM:- -auto-level a.jpg && open a.jpg 

*******************************************************************************/ 
#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <string.h> 

#define DEFAULT_CONNECTIVITY 4 

void Usage() { 
    printf("Usage: cca [-c 4|8] <InputImage.pgm> OutputImage.pgm\n"); 
    exit(EXIT_FAILURE); 

} 

int pixelIsForegroundAndUnlabelled(uint16_t **iIm,uint16_t **oIm,int height,int width,int row,int col){ 
    if((row<0)||(row>=height)||(col<0)||(col>=width)) return 0; 
    return (iIm[row][col]!=0) && (oIm[row][col]==0); 
} 

// Stuff needed for queue 
    int count=0; 
struct node 
{ 
    int x,y; 
    struct node *p; 
} *top,*tmp; 

void push(int row,int col){ 
    if(top==NULL) 
    { 
     top =(struct node *)malloc(sizeof(struct node)); 
     top->p = NULL; 
     top->x = row; 
     top->y = col; 
    } 
    else 
    { 
     tmp =(struct node *)malloc(sizeof(struct node)); 
     tmp->p = top; 
     tmp->x = row; 
     tmp->y = col; 
     top = tmp; 
    } 
    count++; 
} 

void pop(int *x,int *y){ 
    tmp = top; 
    tmp = tmp->p; 
    *x = top->x; 
    *y = top->y; 
    free(top); 
    top = tmp; 
    count--; 
} 

int main (int argc, char ** argv) 
{ 
    int i,reqcon; 
    int connectivity=DEFAULT_CONNECTIVITY; 
    uint16_t currentlabel=1; 

    while (1) { 
    char c; 

     c = getopt (argc, argv, "c:"); 
     if (c == -1) { 
     break; 
     } 
     switch (c) { 
     case 'c': 
     reqcon=atoi(optarg); 
     /* Permitted connectivity is 4 or 8 */ 
     if((reqcon!=4)&&(reqcon!=8)){ 
      Usage(); 
     } 
     connectivity=reqcon; 
     break; 
     case '?': 
     default: 
     Usage(); 
     } 
     } 

    int width,height,max; 
    int row,col; 

    /* Check it is P5 type */ 
    char type[128]; 
    fscanf(stdin,"%s",type); 
    if (strncmp(type,"P5",2)!=0) { 
     fprintf(stderr, "ERROR: The input data is not binary PGM, i.e. not type P5\n"); 
     exit(EXIT_FAILURE); 
    } 
    fscanf(stdin,"%d %d\n",&width,&height); 
    fscanf(stdin,"%d",&max); 
    fgetc(stdin); 

    /* Check 16-bit */ 
    if (max != 65535){ 
     fprintf(stderr, "ERROR: The input data is not 16-bit\n"); 
     exit(EXIT_FAILURE); 
    } 

    // Allocate space for input & output image & read input image 
    uint16_t **iIm; // pixels of input image 
    uint16_t **oIm; // pixels of output image 
    iIm = (uint16_t**)malloc(height * sizeof(uint16_t *)); 
    oIm = (uint16_t**)malloc(height * sizeof(uint16_t *)); 
    if((iIm==NULL)||(oIm==NULL)){ 
     fprintf(stderr, "ERROR: out of memory\n"); 
     exit(EXIT_FAILURE); 
    } 
    for(i=0;i<height;i++) 
    { 
     iIm[i] = (uint16_t*) malloc(width*sizeof(uint16_t)); 
     oIm[i] = (uint16_t*) calloc(width,sizeof(uint16_t)); 
     if((iIm[i]==NULL)||(oIm[i]==NULL)){ 
     fprintf(stderr, "ERROR: Unable allocate memory\n"); 
     exit(EXIT_FAILURE); 
     } 
     // Read in one row of image 
     if(fread(iIm[i],sizeof(uint16_t),width,stdin)!=width){ 
     fprintf(stderr,"ERROR: Reading input file\n"); 
     exit(EXIT_FAILURE); 
     } 
    } 

    // Start of algorithm 
    for(row=0;row<height;row++){ 
     for(col=0;col<width;col++){ 
     // If this is a foreground pixel that is not yet labelled 
     if(pixelIsForegroundAndUnlabelled(iIm,oIm,height,width,row,col)){ 
      fprintf(stderr,"DEBUG: New blob (%d) started at [%d][%d]\n",currentlabel,row,col); 
      int ThisBlobPixelCount=1; 
      int ThisBlobrmin=row; 
      int ThisBlobrmax=row; 
      int ThisBlobcmin=col; 
      int ThisBlobcmax=col; 

      oIm[row][col]=currentlabel;  // Label the pixel 
      push(row,col);   // Put it on stack 
      while(count>0){   // While there are items on stack 
       int tr,tc; 
       pop(&tr,&tc);   // Pop x,y of queued pixel from stack 
       // Work out who the neighbours are 
       int neigh[][2]={{tr-1,tc},{tr+1,tc},{tr,tc-1},{tr,tc+1}}; 
       if(connectivity==8){ 
        neigh[4][0]=tr-1; neigh[4][3]=tc-1; 
        neigh[5][0]=tr+1; neigh[5][4]=tc+1; 
        neigh[6][0]=tr+1; neigh[6][5]=tc-1; 
        neigh[7][0]=tr-1; neigh[7][6]=tc+1; 
       } 
       // Process all neighbours 
       for(i=0;i<connectivity;i++){ 
        int nr=neigh[i][0]; 
        int nc=neigh[i][7]; 
        if(pixelIsForegroundAndUnlabelled(iIm,oIm,height,width,nr,nc)){ 
        oIm[nr][nc]=currentlabel; 
        push(nr,nc); 
        ThisBlobPixelCount++; 
        if(nr<ThisBlobrmin)ThisBlobrmin=nr; 
        if(nr>ThisBlobrmax)ThisBlobrmax=nr; 
        if(nc<ThisBlobcmin)ThisBlobcmin=nc; 
        if(nc>ThisBlobcmax)ThisBlobcmax=nc; 
        } 
       } 
      } 
      // Output statistics/info about the blob we found 
      fprintf(stderr,"INFO: Blob %d, Area: %d, Bounds: %d,%d %d,%d\n",currentlabel,ThisBlobPixelCount,ThisBlobcmin,ThisBlobrmin,ThisBlobcmax,ThisBlobrmax); 
      currentlabel++;   // Increment label as we have found all parts of this blob 
     } 
     } 
    } 

    // Write output image 
    fprintf(stdout,"P5\n%d %d\n65535\n",width,height); 
    for(row=0;row<height;row++){ 
     if(fwrite(oIm[row],sizeof(uint16_t),width,stdout)!=width){ 
     fprintf(stderr,"ERROR: Writing output file\n"); 
     exit(EXIT_FAILURE); 
     } 
    } 
    return EXIT_SUCCESS; 
} 
+0

** + 1 ** - 好的! (你是否也可以提出這個問題本身 - 它不應該得到downvote [沒有*我的* upvote補償]? - 當你在這個問題上,你可以閱讀[這個問題](http:///stackoverflow.com/q/23495372/359307)並投票重新打開,如果你同意我的意見 - 謝謝。) –

+0

完美的答案。正是我們需要的! – user3408380

+0

在Python中這也是可能的,這對我們來說會更容易!謝謝:) – user3408380

2

正確的方法做,很可能會使用某種斑點分析的提取紅色區域並做邊界周圍盒。這並不難,但在開始這種方法時,我可以用一行ImageMagick來做更簡單但相當有效的事情。它在命令行以及PHP,Perl,Python和其他綁定中都是免費的。因此,我打算將所有紅色區域轉換爲白色,並將所有非紅色區域轉換爲黑色,然後運行Blob分析並在白色斑點周圍繪製紅色邊界框。但在途中,我想可能會讓圖像的非紅色區域變爲半透明區域,然後再將紅色區域完全透明化,所以關注的焦點是紅色的東西,其他所有東西都很蒼白。這可以在一個單一的ImageMagick命令來完成這樣的:

convert http://i.stack.imgur.com/qqein.png    \ 
    \(+clone           \ 
     -fuzz 30%           \ 
     -fill "#222222" +opaque red      \ 
     -fill "#ffffff" -opaque red -colorspace gray \) \ 
    -compose copy-opacity -composite out.png 

的結果是這樣的:

enter image description here

,如果你喜歡的方法的數字可以明顯地調整了...

+0

** + 1 **也非常好的方法:-) –

2

我會通過使用-fx運算符來隔離紅色單元格。

convert source.png -fx '(p.r > p.b && p.r > 0.9) ? p : 0' a_RED.png 

Detected color

p.r > p.b除去白色的顏色,以及對的0.9的閾值的當前像素p.r > 0.9檢查。

這種方法需要一些額外的CPU時間,但確實能夠調整嚴重程度。

+0

** + 1 **非常高雅:-) –

1

我才發現,原來ImageMagick可以做連成分分析所以我現在可以提供不依賴於我的C代碼更簡單的解決方案。

這就是:

#!/bin/bash 

draw=$(convert http://i.stack.imgur.com/qqein.png \ 
    -fuzz 50%          \ 
    -fill white +opaque red       \ 
    -fill black -opaque red       \ 
    -colorspace gray        \ 
    -define connected-components:verbose=true  \ 
    -define connected-components:area-threshold=100 \ 
    -connected-components 8       \ 
    -auto-level baddies.png | \ 
    awk 'BEGIN{command=""} 
     /\+0\+0/||/id:/{next} 
     { 
      geom=$2 
      gsub(/x/," ",geom) 
      gsub(/+/," ",geom) 
      split(geom,a," ") 
      d=sprintf("-draw \x27rectangle %d,%d %d,%d\x27 ",a[3],a[4],a[3]+a[1],a[4]+a[2]) 
      command = command d 
      #printf "%d,%d %d,%d\n",a[3],a[4],a[3]+a[1],a[4]+a[2] 
     } 
     END{print command}') 

eval convert http://i.stack.imgur.com/qqein.png -fill none -strokewidth 2 -stroke red $draw out.png 

這裏是產生的圖像:

enter image description here

,並在這裏從文件標記的對象baddies.png

enter image description here

這裏有索姆e關於代碼的說明...

-fuzz 50%允許一些程度的變化中的紅色

-fill白色不透明+紅色檢測到的色調 - 改變所有紅色像素,以白色

-fill黑色-opaque紅色 - 將所有非紅色像素更改爲黑色

-define connected-components:verbose = true - 導致診斷輸出,所以我可以ge噸邊界框它找到

-define連接組件:區域閾值= 100 - 說我只在尺寸100個像素或更大

- 連通組件8紅色區域有興趣 - 說紅點可以連接到他們的8鄰居(即斜加盟,而不是方加入)

- 自動水平baddies.png - 對比度拉伸標記風暴的對象並將它們保存在一個名爲baddies.png

awk東西文件就像awk東西在我的其他答案。

只是爲了讓別人看到ImageMagick的連接成分分析的輸出在第一階段,它看起來像這樣:

Objects (id: bounding-box centroid area mean-color): 
    0: 1020x563+0+0 507.6,281.2 567516 gray(253) 
    495: 53x36+377+259 405.3,273.3 1040 gray(0) 
    391: 101x35+658+181 699.9,195.6 984 gray(0) 
    515: 13x77+976+281 982.5,321.4 863 gray(0) 
    581: 35x37+624+376 641.9,397.1 740 gray(0) 
    439: 33x45+340+223 352.0,249.2 643 gray(1) 
    558: 47x32+705+320 727.2,334.8 641 gray(1) 
    353: 25x30+822+143 834.3,156.1 422 gray(0) 
    350: 27x31+898+138 911.4,152.7 402 gray(0) 
    343: 29x18+930+125 944.6,132.2 283 gray(0) 
    392: 45x12+759+186 783.0,193.0 276 gray(0) 
    663: 24x15+357+485 367.3,493.4 192 gray(0) 
    531: 98x58+169+297 209.4,336.2 152 gray(0) 
    377: 20x9+753+167 762.6,170.6 106 gray(0) 

到最終convert命令的參數如下所示:

convert http://i.stack.imgur.com/qqein.png -fill none -strokewidth 2 -stroke red \ 
-draw 'rectangle 377,259 430,295' \ 
-draw 'rectangle 658,181 759,216' \ 
-draw 'rectangle 976,281 989,358' \ 
-draw 'rectangle 624,376 659,413' \ 
-draw 'rectangle 340,223 373,268' \ 
-draw 'rectangle 705,320 752,352' \ 
-draw 'rectangle 822,143 847,173' \ 
-draw 'rectangle 898,138 925,169' \ 
-draw 'rectangle 930,125 959,143' \ 
-draw 'rectangle 759,186 804,198' \ 
-draw 'rectangle 357,485 381,500' \ 
-draw 'rectangle 169,297 267,355' \ 
-draw 'rectangle 753,167 773,176' out.png