GIF 或 PNG 图像的颜色检测
Color detection on GIF or PNG image
我们想知道是否可以像附图那样做一些事情。
我们的网站上有实时天气雷达,投影在 google 地图页面上,更新周期为 5 分钟。
想法是什么?
我们想为我们的访问者检测 "heavy" 风暴,并用方框或其他东西突出显示它们。如果可能的话,我们想在 PHP 中制作这个系统。我觉得最好的方法是检测颜色什么的?
附上我们用Photoshop绘制的示例图片:
我们希望有人能帮助我们,这样我们就可以开始做一些事情了!
正确的方法可能是使用某种 Blob 分析 来提取红色区域并在它们周围做边界框。这并不难,但在开始这种方法时,我可以用一行 ImageMagick 做一些更简单但非常有效的事情。它是免费的,可以在命令行和 PHP、Perl、Python 和其他绑定中使用。
所以,我打算将所有红色区域转换为白色,将所有 non-red 区域转换为黑色,然后 运行 一个 Blob 分析 并在白色斑点周围绘制红色边界框。但是在途中,我想过也许可以让图像的 non-red 区域 semi-transparent 然后红色区域完全透明,所以注意力集中在红色的东西上,其他的东西真的很苍白.这可以在单个 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
结果是这样的:
如果您喜欢这种方法,显然可以调整数字...
我对此进行了另一次尝试,使用我用 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=+0;if(area>100)print ,}' 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
出来是这样的:
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
结果是这样的:
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;
}
我会使用 -fx 运算符分离红细胞。
convert source.png -fx '(p.r > p.b && p.r > 0.9) ? p : 0' a_RED.png
p.r > p.b
移除白色,p.r > 0.9
根据阈值 0.9
检查当前像素。
此方法需要一些额外的 CPU 时间,但确实可以让您调整严重程度。
我刚刚发现 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=
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
这是生成的图像:
这里是文件 baddies.png
中的标记对象
这里有一些关于代码的注释...
-fuzz 50% 允许检测到的红色阴影有一定程度的变化
-填充白色+不透明红色-将所有红色像素更改为白色
-填充黑色-不透明红色-将所有 non-red 像素更改为黑色
-define connected-components:verbose=true - 导致诊断输出所以我可以获得它找到的边界框
-define connected-components:area-threshold=100 - 表示我只对大小为 100 像素或更大的红色区域感兴趣
-connected-components 8 - 表示红点可以连接到它们的 8 个邻居(即对角线连接,而不是 square-joined)
-auto-level 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
我们想知道是否可以像附图那样做一些事情。
我们的网站上有实时天气雷达,投影在 google 地图页面上,更新周期为 5 分钟。
想法是什么?
我们想为我们的访问者检测 "heavy" 风暴,并用方框或其他东西突出显示它们。如果可能的话,我们想在 PHP 中制作这个系统。我觉得最好的方法是检测颜色什么的?
附上我们用Photoshop绘制的示例图片:
我们希望有人能帮助我们,这样我们就可以开始做一些事情了!
正确的方法可能是使用某种 Blob 分析 来提取红色区域并在它们周围做边界框。这并不难,但在开始这种方法时,我可以用一行 ImageMagick 做一些更简单但非常有效的事情。它是免费的,可以在命令行和 PHP、Perl、Python 和其他绑定中使用。
所以,我打算将所有红色区域转换为白色,将所有 non-red 区域转换为黑色,然后 运行 一个 Blob 分析 并在白色斑点周围绘制红色边界框。但是在途中,我想过也许可以让图像的 non-red 区域 semi-transparent 然后红色区域完全透明,所以注意力集中在红色的东西上,其他的东西真的很苍白.这可以在单个 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
结果是这样的:
如果您喜欢这种方法,显然可以调整数字...
我对此进行了另一次尝试,使用我用 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=+0;if(area>100)print ,}' 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
出来是这样的:
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
结果是这样的:
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;
}
我会使用 -fx 运算符分离红细胞。
convert source.png -fx '(p.r > p.b && p.r > 0.9) ? p : 0' a_RED.png
p.r > p.b
移除白色,p.r > 0.9
根据阈值 0.9
检查当前像素。
此方法需要一些额外的 CPU 时间,但确实可以让您调整严重程度。
我刚刚发现 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=
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
这是生成的图像:
这里是文件 baddies.png
这里有一些关于代码的注释...
-fuzz 50% 允许检测到的红色阴影有一定程度的变化
-填充白色+不透明红色-将所有红色像素更改为白色
-填充黑色-不透明红色-将所有 non-red 像素更改为黑色
-define connected-components:verbose=true - 导致诊断输出所以我可以获得它找到的边界框
-define connected-components:area-threshold=100 - 表示我只对大小为 100 像素或更大的红色区域感兴趣
-connected-components 8 - 表示红点可以连接到它们的 8 个邻居(即对角线连接,而不是 square-joined)
-auto-level 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