【问题标题】:Querying Colorspace in a Database查询数据库中的色彩空间
【发布时间】:2011-07-27 06:54:29
【问题描述】:

我正在使用 Django,并且有一个 MySQL 数据库,其中填充了任意 RGB 颜色。 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

毕达哥拉斯告诉我们,对于这个范围内的所有点,以下情况都是正确的:

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

我想把它翻译成一个有效的 Django 查询。并且,失败了,一个 MySQL 查询。

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


Django 颜色模型看起来像:

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

【问题讨论】:

    标签: mysql sql django django-queryset


    【解决方案1】:

    我的大脑现在很紧张,所以确切的语法有点不对劲,但是,假设您在 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 经验和更多睡眠的人比我能建立在此基础上的 :-)

    【讨论】:

      【解决方案2】:

      很简单,

      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"
      

      【讨论】:

      • 哇,真的,这行得通吗?我想说 Django Query 语言的限制是一种轻描淡写的说法。
      • @Chris / 当然你需要使用正确的语法,POW() 函数而不是 SQL-Server ^ 运算符!
      【解决方案3】:

      创建此查询的 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 的,因此这与数据库无关。

      【讨论】:

        【解决方案4】:

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

        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])
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-20
          • 2018-08-19
          • 2013-01-02
          • 2019-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多