查询数据库中的色彩空间

时间:2011-03-31 22:07:24

标签: mysql sql django django-queryset

我正在使用Django并且拥有一个填充了任意RGB颜色的MySQL数据库。 RGB值存储为整数。

| Name  | R | G | B |
+-------+---+---+---+
| Green | 0 |255| 0 |
| Cyan  | 0 |255|255|
| ...   | . | . | . |
| Foo   |123| 45| 2 |
| ...   | . | . | . |

给定任意RGB值(a,b,c)我想计算数据库中哪些颜色“接近”(a,b,c)。我将在我的查询中预定“接近”的含义,但现在让我们称之为x

您可以将RGB颜色空间视为三维空间,颜色为该空间中的点。因此(a,b,c)x在该空间中定义了一个带有中心点(a,b,c)和半径x的球体。

Pythagorus告诉我们,这个领域内的所有要点都是如此:

(R-a)**2 + (G-b)**2 + (B-c)**2 <= x**2

我想将其转换为有效的Django Query。而且,如果没有,那就是MySQL查询。

我不是MySQL专家,但我怀疑Django Query语法在这种情况下可能非常有限。写一个原始SQL查询会成为这里的方式吗?它会更好,因为代码会更清晰吗?它真的可以更快/更有效吗?


Django Color Model看起来像:

class Color(models.Model):
    name = models.CharField(max_length=32)
    r = models.IntegerField()
    g = models.IntegerField()
    b = models.IntegerField()

示例查询:

c = (234, 23, 45)
x = 25

nearby_colors = Color.objects.filter(....) # Awesome-sauce

4 个答案:

答案 0 :(得分:2)

创建此查询的django ORM方式将类似于:

result = Color.objects.extra(
        where=['POWER(%d-a,2) + POWER(%d-b,2) + POWER(%d-c,2) <= POWER(%d,2)'  % (R,G,B,x)]
        ).all()

如果您使用变量R = 50,G = 50,B = 50,x = 3(即str(result.query))打印生成的查询,那么您将生成:

SELECT "whatever_color"."id", "whatever_color"."name", "whatever_color"."r",
        "whatever_color"."g", "whatever_color"."b" 
    FROM "whatever_color" 
    WHERE POWER(50-a,2) + POWER(50-b,2) + POWER(50-c,2) <= POWER(3,2)

请注意,POWER()函数是特定于mysql的,因此这与数据库无关。

答案 1 :(得分:1)

简单地说,

select *
from color
where POW(R-a,2) + POW(G-b,2) + POW(B-c,2) <= POW(x,2)

其中R,G,B是列,您将提供要替换的值,a,b,c,x

要测试的一些样本数据

create table color(r int, g int, b int);
insert color values (200,50,200);
insert color values (0,50,200);
insert color values (0,50,20);
insert color values (150,150,200);
insert color values (200,50,0);
insert color values (50,50,50);
insert color values (40,60,40);
insert color values (50,50,101);  # 101-50 = 51 > 50 on the B-value
insert color values (50,50,100);  # just
insert color values (50,50,99);   # inside = ok
insert color values (40,60,40);
insert color values (70,70,70);
insert color values (85,80,75);  # 35 / 30 / 25 => 2750 > 2500

查询,来自(50,50,50)的50个单位

select *
from color
where POW(R-50,2) + POW(G-50,2) + POW(B-50,2) <= POW(50,2)

输出

"r";"g";"b"
"50";"50";"50"
"40";"60";"40"
"50";"50";"100"
"50";"50";"99"
"40";"60";"40"
"70";"70";"70"

答案 2 :(得分:1)

感谢所有提供意见的人,但我认为我的解决方案与建议我应该创建自己的答案的方式不同。

def build_color_query(sphere_color_range):

    c = sphere_color_range[:3] # Sphere center
    r2 = sphere_color_range[3]**2 # Radius-squared

    # Use the "POWER" function is the database is MySQL
    if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.mysql':

        color_query = """POWER((tcolor.r - %(a)s),2)
         + POWER((color.g - %(b)s),2)
         + POWER((color.b - %(c)s),2) <= %(r2)s""" % ({
            'a':str(c[0]),
            'b':str(c[1]),
            'c':str(c[2]),
            'r2':str(r2),
        })

    # Otherwise we use multiplication
    else:

        color_query = """(color.r - %(a)s) * (color.r - %(a)s)
         + (color.g - %(b)s) * (color.g - %(b)s)
         + (color.b - %(c)s) * (color.b - %(c)s) <= %(r2)s""" % ({
            'a':str(c[0]),
            'b':str(c[1]),
            'c':str(c[2]),
            'r2':str(r2),
        })

    # I had to include the `.filter(r__gte=0)` here in order for the 
    # right table joins to have been performed for me `extra` to work.
    # (It may not be necessary in this simplified version)
    return Color.objects.filter(r__gte=0).extra(where=[color_query])

答案 3 :(得分:0)

我的大脑现在已经被炒了,所以确切的语法有点偏离,但是,假设你有R,G和B的索引,做三个查询,每个索引一个,并将它们连接在一起。

SELECT * FROM COLOR color
         JOIN (SELECT * FROM COLORS WHERE (color.R-a) < threshold)
         JOIN (SELECT * FROM COLORS WHERE (color.G-b) < threshold)
         WHERE (color.B-c) < threshold

您需要确保不允许空值的连接类型。我忘了这是否有效。

但是那些拥有更好的sql经验和更多睡眠的人可以建立在此基础之上: - )