What I would do is defining a structure for the 2D array in term of its:
- number of columns
- number of rows
- pointer to array data in memory
Note that I would "linearize" the array, i.e. allocate a single block of memory of size Columns * Rows * sizeof(int), and given the i and j row and column index, these two indexes can be converted with simple math to a single index in the 1D array (e.g. index = rowIndex * Columns + columnIndex)
Then, I would just return a pointer to this structure from your ReadTxtFile function:
struct IntArray2D {
    int Rows;
    int Columns;
    int* Elements;
};
/* 
 * Define a couple of helper functions to allocate 
 * and free the IntArray2D structure. 
 */
struct IntArray2D* IntArray2D_Create(int rows, int columns);
void IntArray2D_Free(struct IntArray2D* array);
/* 
 * On success, returns a 2D array with data read from file.
 * On failure, returns NULL.
 * NOTE: callers must call IntArray2D_Free() when done 
 */
struct IntArray2D* ReadTxtFile(const char* filename);
EDIT As an alternative, you could define the array structure has having a header block with rows and columns count, immediately followed by the "linearized" 2D array elements, using a "flexible array member": 
struct IntArray2D {
    int Rows;
    int Columns;
    int Elements[];
};
You can then define some convenient functions to operate on this custom array structure, e.g.:
struct IntArray2D* IntArray2D_Create(int rows, int columns)
{
    /* Check rows and columns parameters are > 0 */
    /* ... */
    struct IntArray2D *p = malloc(sizeof(struct IntArray2D)
                                  + rows * columns * sizeof(int));
    if (p == NULL) {
        return NULL;
    }
    p->Rows = rows;
    p->Columns = columns;
    /* May zero out the array elements or not... */
    memset(p->Elements, 0, rows * columns * sizeof(int));
    return p; 
}
void IntArray2D_Free(struct IntArray2D* array)
{
    free(array);
}
int IntArray2D_GetElement(struct IntArray2D* array, 
                                 int row, int column)
{
    /* Check array is not NULL; check row and column 
       indexes are in valid ranges ... */
    int index = row * (array->Columns) + column;
    return array->Elements[index];
}
void IntArray2D_SetElement(struct IntArray2D* array, 
                                  int row, int column,
                                  int value)
{
    /* Check array is not NULL; check row and column 
       indexes are in valid ranges ... */
    int index = row * (array->Columns) + column;
    array->Elements[index] = value;
}
Inside your ReadTxtFile function, instead of calling malloc, you can call IntArray2D_Create:
struct IntArray2D* ReadTxtFile(const char* filename) 
{ 
    struct IntArray2D* data = NULL;
    /* ... */
    rows = getRows(fp);
    cols = getCols(fp);
    data = IntArray2D_Create(rows, cols);
    if (data == NULL) {
        /* Handle error ... */ 
        return NULL;
    }
    /* Fill the array ... */
In particular, instead your:
if(EOF==fscanf(fp, "%d", &H[r][c])){
you can do:
    /* int valueReadFromFile */
    if (EOF == fscanf(fp, "%d", &valueReadFromFile)) {
        fprintf(stderr, "The data is insufficient.\n");
    }      
    IntArray2D_SetElement(data, r, c, valueReadFromFile);
And then at the end of the function, you can just have:
    return data;
}