Get texture image dpi
DPI of a texture/image will depend on how many pixels are mapped onto a polygon and the real-world area of that polygon. Now your question is more exact (homogeneous uv - no stretching), some parts of this answer aren't directed at you but hope to describe the notion of DPI and that it won't always be a single figure per model.

The following only works if your image-texture is square. width px is the same as height px

factors that decide DPI

You can use the same texture on large polygons and on small polygons, you can also scale the uv coordinates so a small polygon could contain many pixels (effectively high 'dpi'). The DPI therefor is not a property of the texture, but of each polygon and uv mapping. 

If you have no such stretching going on and each uvmapped face is covered by the same amount of pixels relative to its real-world area, then you can calculate the DPI once. 



Variable DPI vs Homogeneous DPI

The effective DPI of a texture is often variable, but not always. Indeed for certain kinds of texturing it is desirable to have no stretching at all. In either scenario the formula is the same, but when you speak of homogeneous uv-mapping you only need to calculate it once.

Finding UV coordinates

Code to help with this can be found in TextEditor -> Templates -> Python -> Operator Mesh UV. Below i've stripped out the template to make sure it just prints the 2d coordinates of each .uv (a 2D .uv corresponds to a 3D vertex.co). This would give you the 2d coordinate in the UVmap of each vertex per polygon:

import bpy
import bmesh

obj = bpy.context.edit_object
me = obj.data
bm = bmesh.from_edit_mesh(me)

uv_layer = bm.loops.layers.uv.verify()
bm.faces.layers.tex.verify()

for f in bm.faces:
    for l in f.loops:
        luv = l[uv_layer]
        print(luv.uv)


Here's the formula used to calculate the area given a list of coordinates, here's the same formula stated in Python. From the Area you can extract a hypothetical side length.

import math

import bpy
import bmesh
from mathutils import Vector


def texture_dpi(polygon_area, uv_area, image_dim):
    """
    polygon_area:   in (meters or BU)
    uv_area:        in ratio (0..1)
    image_dim:      either width or height of image, this assumes
                    square images anyway, else none of this makes sense
    """

    l1 = math.sqrt(polygon_area)
    l2 = math.sqrt(uv_area)

    # assume the units are BU or meter.
    inches = l1 * 39.3701
    pixels = int(l2 * image_dim)
    return int(pixels / inches)


def calc_area_from_2d_vectorlist(v):
    # http://www.mathopenref.com/coordpolygonarea.html
    sum = 0
    n = len(v)
    for i in range(n - 1):
        sum += ((v[i].x * v[i + 1].y) - (v[i].y * v[i + 1].x))
    sum += ((v[n - 1].x * v[0].y) - (v[n - 1].y * v[0].x))
    return abs(sum / 2)


def get_bm_from_edit_object(image_name):
    obj = bpy.context.edit_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)

    uv_layer = bm.loops.layers.uv.verify()
    bm.faces.layers.tex.verify()

    totals = []
    for f in bm.faces:
        vl = [l[uv_layer].uv for l in f.loops]
        fa = calc_area_from_2d_vectorlist(vl)  # 2d area of uv loop
        totals.append([f.calc_area(), fa])     # 2d area of face (local size)
        break  # if homogeneous  , else comment it out.

    image_dim = bpy.data.images[image_name].size[0]
    for polygon_area, uv_area in totals:
        dpi = texture_dpi(polygon_area, uv_area, image_dim)
        print(dpi)

get_bm_from_edit_object('some_texture.png')