Запрос цветового пространства в базе данных - PullRequest
2 голосов
/ 01 апреля 2011

Я использую 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

Я бы хотел перевести это в действительный запрос Джанго. И, в случае неудачи, запрос MySQL.

Я не эксперт по MySQL, но у меня есть некоторые подозрения, что синтаксис запросов Django может быть очень ограничивающим в этом контексте. Будет ли писать необработанный 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 ]

2 голосов
/ 01 апреля 2011

Способ создания этого запроса в 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 голос
/ 20 апреля 2011

Спасибо всем, кто предоставил информацию, но я подумал, что мое решение достаточно отличается от того, что было предложено, чтобы я создал свой собственный ответ.

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])
1 голос
/ 01 апреля 2011

Просто

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"
0 голосов
/ 01 апреля 2011

Мой мозг сейчас исчерпан, поэтому точный синтаксис немного неправильный, но, если у вас есть индекс для 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 и большим количеством сна, чем я, мог бы построить это: -)

...